From bab720b88e34f1b76ce3e4d1b0c2a8e3528c187e Mon Sep 17 00:00:00 2001 From: alick-liming Date: Tue, 27 Oct 2026 10:44:37 +0800 Subject: [PATCH 1/2] feat: add channel-owned dedicated integrations --- README.md | 13 + README_zh.md | 13 + dedicated_integrations.go | 105 +++ dedicated_integrations_test.go | 151 ++++ integrations.go | 16 +- internal/cmd/gen/main.go | 15 +- internal/cmd/gen/main_test.go | 15 + models_gen.go | 102 ++- openapi/openapi.en.json | 1500 ++++++++++++++++++++++++++++---- openapi/openapi.zh.json | 1500 ++++++++++++++++++++++++++++---- roundtrip_gen_test.go | 5 + services_gen.go | 2 + 12 files changed, 3064 insertions(+), 373 deletions(-) create mode 100644 dedicated_integrations.go create mode 100644 dedicated_integrations_test.go diff --git a/README.md b/README.md index 3622c3f..6e5c6b6 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,19 @@ To call the API with an OAuth access token instead, use `NewClientWithAccessToke client, err := flashduty.NewClientWithAccessToken(accessToken) ``` +## Public and dedicated integrations + +Use `client.Integrations` for public integrations managed in the integration center. They can be connected to channels through subscriptions or routing and require integration permissions. Use `client.DedicatedIntegrations` for integrations owned by one channel; they require channel permissions, including access to that specific channel, rather than integration-center permissions. + +```go +req := &flashduty.ChannelIntegrationListRequest{ChannelID: 20001} +integrations, _, err := client.DedicatedIntegrations.ReadList(ctx, req) +``` + +Choose the API from the integration's ownership. An integration ID alone does not identify its scope. The SDK does not switch API groups on a permission error. Both groups authenticate with the management App Key; the returned `integration_key` authenticates inbound events. List/info include that key and masked settings, so avoid logging complete responses. + +Dedicated updates preserve omitted fields. Set `Description` to a pointer to `""` to clear it; nil keeps it. A nil `Settings` map preserves configuration; a non-nil empty map submits `{}` as a replacement, subject to the type's validation and secret-preservation rules. + ## Design - **Thin and typed.** Every method maps to exactly one HTTP call and returns `(*T, *Response, error)`. No hidden cross-endpoint enrichment. diff --git a/README_zh.md b/README_zh.md index 6dac5b2..270aa60 100644 --- a/README_zh.md +++ b/README_zh.md @@ -64,6 +64,19 @@ func main() { client, err := flashduty.NewClientWithAccessToken(accessToken) ``` +## 公共集成与专属集成 + +集成中心管理的公共集成使用 `client.Integrations`,可通过订阅或路由接入协作空间,需要集成中心权限。属于某个协作空间的专属集成使用 `client.DedicatedIntegrations`,需要协作空间权限以及对目标空间的访问或管理权限,不需要集成中心权限。 + +```go +req := &flashduty.ChannelIntegrationListRequest{ChannelID: 20001} +integrations, _, err := client.DedicatedIntegrations.ReadList(ctx, req) +``` + +按集成的实际归属选择接口,不能仅凭集成 ID 判断类型。权限不足时 SDK 不会自动切换接口。两类接口均使用管理用的 App Key 认证;响应里的 `integration_key` 用于事件上报。列表和详情包含完整的集成 Key 及脱敏配置,避免记录完整响应。 + +专属集成更新时,省略字段会保留原值。`Description` 为 nil 时保留原值,指向空字符串时清空描述;`Settings` 为 nil 时保留配置,非 nil 的空 map 会提交 `{}` 作为替换配置,仍受该集成类型的校验和敏感字段保留规则约束。 + ## 设计 - **薄且强类型。** 每个方法只对应一次 HTTP 调用,返回 `(*T, *Response, error)`,不会在背后跨接口补数据。 diff --git a/dedicated_integrations.go b/dedicated_integrations.go new file mode 100644 index 0000000..b76c78c --- /dev/null +++ b/dedicated_integrations.go @@ -0,0 +1,105 @@ +// Code generated by internal/cmd/gen; DO NOT EDIT. + +package flashduty + +import "context" + +// DedicatedIntegrationsService handles the "On-call/Dedicated integrations" API resource. +type DedicatedIntegrationsService service + +// Get dedicated integration detail. +// +// Get one channel-owned integration, including its integration key and masked settings. +// +// API: POST /channel/integration/info (channel-integration-read-info). +func (s *DedicatedIntegrationsService) ReadInfo(ctx context.Context, req *ChannelIntegrationIDRequest) (*ChannelIntegrationItem, *Response, error) { + out := new(ChannelIntegrationItem) + resp, err := s.client.do(ctx, "/channel/integration/info", req, out) + if err != nil { + return nil, resp, err + } + return out, resp, nil +} + +// List dedicated integrations. +// +// List the dedicated integrations owned by one channel. +// +// API: POST /channel/integration/list (channel-integration-read-list). +func (s *DedicatedIntegrationsService) ReadList(ctx context.Context, req *ChannelIntegrationListRequest) (*ChannelIntegrationListResponse, *Response, error) { + out := new(ChannelIntegrationListResponse) + resp, err := s.client.do(ctx, "/channel/integration/list", req, out) + if err != nil { + return nil, resp, err + } + return out, resp, nil +} + +// Create dedicated integration. +// +// Create a dedicated alert integration for an enabled channel. +// +// API: POST /channel/integration/create (channel-integration-write-create). +func (s *DedicatedIntegrationsService) WriteCreate(ctx context.Context, req *ChannelIntegrationCreateRequest) (*ChannelIntegrationCreateResponse, *Response, error) { + out := new(ChannelIntegrationCreateResponse) + resp, err := s.client.do(ctx, "/channel/integration/create", req, out) + if err != nil { + return nil, resp, err + } + return out, resp, nil +} + +// Delete dedicated integration. +// +// Delete a dedicated integration after its references have been removed. +// +// API: POST /channel/integration/delete (channel-integration-write-delete). +func (s *DedicatedIntegrationsService) WriteDelete(ctx context.Context, req *ChannelIntegrationIDRequest) (*Response, error) { + return s.client.do(ctx, "/channel/integration/delete", req, nil) +} + +// Disable dedicated integration. +// +// Disable a dedicated integration to pause alert ingestion. +// +// API: POST /channel/integration/disable (channel-integration-write-disable). +func (s *DedicatedIntegrationsService) WriteDisable(ctx context.Context, req *ChannelIntegrationIDRequest) (*Response, error) { + return s.client.do(ctx, "/channel/integration/disable", req, nil) +} + +// Enable dedicated integration. +// +// Enable a dedicated integration so it can receive alerts. +// +// API: POST /channel/integration/enable (channel-integration-write-enable). +func (s *DedicatedIntegrationsService) WriteEnable(ctx context.Context, req *ChannelIntegrationIDRequest) (*Response, error) { + return s.client.do(ctx, "/channel/integration/enable", req, nil) +} + +// Rotate dedicated integration key. +// +// Issue a new dedicated integration key and invalidate the previous key. +// +// API: POST /channel/integration/key/rotate (channel-integration-write-rotate-key). +func (s *DedicatedIntegrationsService) WriteRotateKey(ctx context.Context, req *ChannelIntegrationIDRequest) (*RotateIntegrationKeyResponse, *Response, error) { + out := new(RotateIntegrationKeyResponse) + resp, err := s.client.do(ctx, "/channel/integration/key/rotate", req, out) + if err != nil { + return nil, resp, err + } + return out, resp, nil +} + +// Update dedicated integration. +// +// Update the name, description, or settings of a dedicated integration. +// +// API: POST /channel/integration/update (channel-integration-write-update). +func (s *DedicatedIntegrationsService) WriteUpdate(ctx context.Context, req *ChannelIntegrationUpdateRequest) (*ChannelIntegrationItem, *Response, error) { + out := new(ChannelIntegrationItem) + resp, err := s.client.do(ctx, "/channel/integration/update", req, out) + if err != nil { + return nil, resp, err + } + return out, resp, nil +} diff --git a/dedicated_integrations_test.go b/dedicated_integrations_test.go new file mode 100644 index 0000000..74b7b9e --- /dev/null +++ b/dedicated_integrations_test.go @@ -0,0 +1,151 @@ +package flashduty + +import ( + "context" + "encoding/json" + "net/http" + "reflect" + "testing" +) + +func TestDedicatedIntegrationRequests(t *testing.T) { + for _, tc := range []struct { + name string + body string + call func(*Client) error + }{ + {"list", `{"channel_id":20001,"p":2,"limit":10,"name":"Primary","plugin_type":"standard.alert","status":"enabled"}`, func(c *Client) error { + _, _, err := c.DedicatedIntegrations.ReadList(context.Background(), &ChannelIntegrationListRequest{ListOptions: ListOptions{Page: 2, Limit: 10}, ChannelID: 20001, Name: "Primary", PluginType: "standard.alert", Status: "enabled"}) + return err + }}, + {"info", `{"integration_id":10001}`, func(c *Client) error { + _, _, err := c.DedicatedIntegrations.ReadInfo(context.Background(), &ChannelIntegrationIDRequest{IntegrationID: 10001}) + return err + }}, + {"create", `{"channel_id":20001,"plugin_type":"standard.alert","name":"Primary","integration_key":"0123456789abcdef0123456789abcdef100","settings":{"headers":{}}}`, func(c *Client) error { + _, _, err := c.DedicatedIntegrations.WriteCreate(context.Background(), &ChannelIntegrationCreateRequest{ChannelID: 20001, PluginType: "standard.alert", Name: "Primary", IntegrationKey: "0123456789abcdef0123456789abcdef100", Settings: map[string]any{"headers": map[string]any{}}}) + return err + }}, + {"update", `{"integration_id":10001,"description":""}`, func(c *Client) error { + empty := "" + _, _, err := c.DedicatedIntegrations.WriteUpdate(context.Background(), &ChannelIntegrationUpdateRequest{IntegrationID: 10001, Description: &empty}) + return err + }}, + {"delete", `{"integration_id":10001}`, func(c *Client) error { + _, err := c.DedicatedIntegrations.WriteDelete(context.Background(), &ChannelIntegrationIDRequest{IntegrationID: 10001}) + return err + }}, + {"enable", `{"integration_id":10001}`, func(c *Client) error { + _, err := c.DedicatedIntegrations.WriteEnable(context.Background(), &ChannelIntegrationIDRequest{IntegrationID: 10001}) + return err + }}, + {"disable", `{"integration_id":10001}`, func(c *Client) error { + _, err := c.DedicatedIntegrations.WriteDisable(context.Background(), &ChannelIntegrationIDRequest{IntegrationID: 10001}) + return err + }}, + {"key/rotate", `{"integration_id":10001}`, func(c *Client) error { + _, _, err := c.DedicatedIntegrations.WriteRotateKey(context.Background(), &ChannelIntegrationIDRequest{IntegrationID: 10001}) + return err + }}, + } { + t.Run(tc.name, func(t *testing.T) { + c := newTestClient(t, func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost || r.URL.Path != "/channel/integration/"+tc.name || r.URL.Query().Get("app_key") != "KEY" { + t.Errorf("unexpected request: %s %s", r.Method, r.URL) + } + var want, got map[string]any + err := json.Unmarshal([]byte(tc.body), &want) + if err != nil { + t.Error(err) + } + err = json.NewDecoder(r.Body).Decode(&got) + if err != nil { + t.Error(err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("body = %#v, want %#v", got, want) + } + _, _ = w.Write([]byte(`{"data":{}}`)) + }) + err := tc.call(c) + if err != nil { + t.Fatal(err) + } + }) + } +} + +func TestDedicatedIntegrationUpdatePreservesEmptySettings(t *testing.T) { + for _, tc := range []struct { + name string + settings map[string]any + present bool + }{ + {"omitted", nil, false}, + {"empty replacement", map[string]any{}, true}, + {"masked secret", map[string]any{"password": "******"}, true}, + } { + t.Run(tc.name, func(t *testing.T) { + body, err := json.Marshal(&ChannelIntegrationUpdateRequest{IntegrationID: 10001, Settings: tc.settings}) + if err != nil { + t.Fatal(err) + } + var got map[string]any + _ = json.Unmarshal(body, &got) + _, present := got["settings"] + if present != tc.present { + t.Fatalf("settings presence = %v, want %v: %s", present, tc.present, body) + } + if present && !reflect.DeepEqual(got["settings"], tc.settings) { + t.Fatalf("settings changed: %s", body) + } + }) + } +} + +func TestDedicatedIntegrationResponsePreservesOwnershipAndConfiguration(t *testing.T) { + const item = `{"integration_id":10001,"channel_id":20001,"integration_key":"fixture-key","exclusive_data_source_id":0,"plugin_id":9,"plugin_type":"standard.alert","plugin_type_name":"Standard","category":"event.alert","name":"Primary","description":"","status":"enabled","created_at":1700000000,"updated_at":1700000001,"last_time":0,"last_source_time":0,"settings":{"password":"******"}}` + c := newTestClient(t, func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte(`{"data":{"items":[` + item + `],"total":1,"p":1,"limit":100}}`)) + }) + out, _, err := c.DedicatedIntegrations.ReadList(context.Background(), &ChannelIntegrationListRequest{ChannelID: 20001}) + if err != nil { + t.Fatal(err) + } + if out.Total != 1 || len(out.Items) != 1 { + t.Fatalf("unexpected page: %#v", out) + } + encoded, err := json.Marshal(out.Items[0]) + if err != nil { + t.Fatal(err) + } + var want, got map[string]any + _ = json.Unmarshal([]byte(item), &want) + _ = json.Unmarshal(encoded, &got) + if out.Items[0].CreatedAt.Unix() != 1700000000 || out.Items[0].UpdatedAt.Unix() != 1700000001 { + t.Fatalf("timestamps changed: %#v", out.Items[0]) + } + for _, field := range []string{"created_at", "updated_at"} { + delete(want, field) + delete(got, field) + } + if !reflect.DeepEqual(got, want) { + t.Fatalf("response fields changed: got %s, want %s", encoded, item) + } +} + +func TestDedicatedIntegrationForbiddenDoesNotCallPublicAPI(t *testing.T) { + calls := 0 + c := newTestClient(t, func(w http.ResponseWriter, r *http.Request) { + calls++ + if r.URL.Path != "/channel/integration/info" { + t.Errorf("unexpected fallback: %s", r.URL.Path) + } + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"error":{"code":"AccessDenied","message":"Channel access denied"}}`)) + }) + _, _, err := c.DedicatedIntegrations.ReadInfo(context.Background(), &ChannelIntegrationIDRequest{IntegrationID: 10001}) + if ErrorCodeOf(err) != ErrorCodeAccessDenied || calls != 1 { + t.Fatalf("error = %v, calls = %d", err, calls) + } +} diff --git a/integrations.go b/integrations.go index 2631042..8b78b3a 100644 --- a/integrations.go +++ b/integrations.go @@ -21,7 +21,7 @@ func (s *IntegrationsService) DatasourceImPersonTryLink(ctx context.Context, req return out, resp, nil } -// Get integration detail. +// Get public integration detail. // // Get a public integration with its stored settings and decrypted integration key. // @@ -35,7 +35,7 @@ func (s *IntegrationsService) IntegrationAPIReadInfo(ctx context.Context, req *G return out, resp, nil } -// List integrations. +// List public integrations. // // List public integrations with their stored settings, decrypted keys, and edit permissions. // @@ -63,7 +63,7 @@ func (s *IntegrationsService) IntegrationAPIReadTypeList(ctx context.Context, re return out, resp, nil } -// Create integration. +// Create public integration. // // Create a public integration for a supported alert, change, IM, or webhook type. // @@ -77,7 +77,7 @@ func (s *IntegrationsService) IntegrationAPIWriteCreate(ctx context.Context, req return out, resp, nil } -// Delete integration. +// Delete public integration. // // Delete an integration that nothing else references. // @@ -86,7 +86,7 @@ func (s *IntegrationsService) IntegrationAPIWriteDelete(ctx context.Context, req return s.client.do(ctx, "/integration/delete", req, nil) } -// Disable integration. +// Disable public integration. // // Disable an integration without deleting its configuration. // @@ -95,7 +95,7 @@ func (s *IntegrationsService) IntegrationAPIWriteDisable(ctx context.Context, re return s.client.do(ctx, "/integration/disable", req, nil) } -// Enable integration. +// Enable public integration. // // Re-enable a disabled integration so it accepts events again. // @@ -104,7 +104,7 @@ func (s *IntegrationsService) IntegrationAPIWriteEnable(ctx context.Context, req return s.client.do(ctx, "/integration/enable", req, nil) } -// Rotate integration key. +// Rotate public integration key. // // Issue a new integration key and invalidate the previous one. // @@ -118,7 +118,7 @@ func (s *IntegrationsService) IntegrationAPIWriteRotateKey(ctx context.Context, return out, resp, nil } -// Update integration. +// Update public integration. // // Update an integration's name, description, team, or settings. // diff --git a/internal/cmd/gen/main.go b/internal/cmd/gen/main.go index 13feae9..9be6360 100644 --- a/internal/cmd/gen/main.go +++ b/internal/cmd/gen/main.go @@ -875,8 +875,8 @@ func (g *Gen) emitStruct(name string, s map[string]any) string { case inReq && needsPointer && (isNullable(pv) || preserveAbsence): jsonTag = k + ",omitempty" toonTag = k + ",omitempty" - case inReq && !required[k] && preserveAbsence && strings.HasPrefix(gt, "[]"): - // Preserve nil (omitted) versus an explicit empty slice (clear). + case inReq && !required[k] && ((preserveAbsence && strings.HasPrefix(gt, "[]")) || (isNullable(pv) && strings.HasPrefix(gt, "map["))): + // Preserve nil (omitted) versus an explicit empty container (clear). // omitempty would drop both and silently keep the stored value. jsonTag = k + ",omitzero" toonTag = k + ",omitempty" @@ -925,11 +925,14 @@ func (g *Gen) goTypeOf(s map[string]any, hint string) string { } return goName(ref) } - // A oneOf/anyOf is a polymorphic value (e.g. bool | string | string[]); its - // only safe Go type is any. Returning map[string]any here would be wrong for - // non-object members. + // Scalar alternatives can refine an explicit type (e.g. empty or a minimum + // string length). Other unions remain polymorphic, so their safe type is any. if len(asSlice(s["oneOf"])) > 0 || len(asSlice(s["anyOf"])) > 0 { - return "any" + switch typeStr(s) { + case "string", "integer", "number", "boolean": + default: + return "any" + } } if len(asSlice(s["allOf"])) > 0 { return g.queue(hint, g.mergeAllOf(s)) diff --git a/internal/cmd/gen/main_test.go b/internal/cmd/gen/main_test.go index 1ba387f..7f80b5c 100644 --- a/internal/cmd/gen/main_test.go +++ b/internal/cmd/gen/main_test.go @@ -5,6 +5,21 @@ import ( "testing" ) +func TestGoTypeOfStringWithLengthAlternatives(t *testing.T) { + g := newTestGen(nil) + schema := map[string]any{ + "type": "string", + "anyOf": []any{ + map[string]any{"maxLength": 0}, + map[string]any{"minLength": 2}, + }, + } + got := g.goTypeOf(schema, "Name") + if got != "string" { + t.Fatalf("type = %s, want string", got) + } +} + // newTestGen returns a Gen with every map field initialized, mirroring run()'s // construction, so g.queue (invoked while resolving inline nullable objects) // and g.isStructSchema (which walks g.schemas for $ref targets) don't panic diff --git a/models_gen.go b/models_gen.go index 89423b8..86c868c 100644 --- a/models_gen.go +++ b/models_gen.go @@ -2008,6 +2008,106 @@ type ChannelInfosResponse struct { Items []ChannelShort `json:"items" toon:"items"` } +// ChannelIntegrationCreateRequest is generated from the Flashduty OpenAPI schema. +type ChannelIntegrationCreateRequest struct { + // Target channel ID; the channel must be enabled. + ChannelID int64 `json:"channel_id" toon:"channel_id"` + // Description, at most 499 characters. + Description string `json:"description,omitempty" toon:"description,omitempty"` + // Optional supplied integration key. A non-empty value must have 35 characters, match the account suffix, and be unique. + IntegrationKey string `json:"integration_key,omitempty" toon:"integration_key,omitempty"` + // Integration name, 2–49 characters when supplied; omitted or empty uses the integration type default. + Name string `json:"name,omitempty" toon:"name,omitempty"` + // Enabled alert integration type identifier, such as `standard.alert`. + PluginType string `json:"plugin_type" toon:"plugin_type"` + // Integration-type-specific configuration; saved secret values in responses are masked as `******`. + Settings map[string]any `json:"settings,omitempty" toon:"settings,omitempty"` +} + +// ChannelIntegrationCreateResponse is generated from the Flashduty OpenAPI schema. +type ChannelIntegrationCreateResponse struct { + // Created dedicated integration ID. + IntegrationID int64 `json:"integration_id" toon:"integration_id"` + // Integration key for alert ingestion. + IntegrationKey string `json:"integration_key" toon:"integration_key"` +} + +// ChannelIntegrationIDRequest is generated from the Flashduty OpenAPI schema. +type ChannelIntegrationIDRequest struct { + // Dedicated integration ID returned by create, list, or info. Public integration IDs are not accepted. + IntegrationID int64 `json:"integration_id" toon:"integration_id"` +} + +// ChannelIntegrationItem is generated from the Flashduty OpenAPI schema. +type ChannelIntegrationItem struct { + // Integration category; dedicated integration creation accepts `event.alert` alert integrations. + Category string `json:"category" toon:"category"` + // ID of the channel that owns the dedicated integration. + ChannelID int64 `json:"channel_id" toon:"channel_id"` + // Creation time as a Unix timestamp in seconds. + CreatedAt Timestamp `json:"created_at" toon:"created_at"` + // Integration description; empty when unset. + Description string `json:"description" toon:"description"` + // Compatibility identifier for earlier integrations; `0` when absent. Use `integration_id` for this API group. + ExclusiveDataSourceID int64 `json:"exclusive_data_source_id" toon:"exclusive_data_source_id"` + // Dedicated integration ID; use this ID for subsequent API calls. + IntegrationID int64 `json:"integration_id" toon:"integration_id"` + // Complete integration key for alert ingestion; separate from the management App Key. + IntegrationKey string `json:"integration_key" toon:"integration_key"` + // Source time of the most recent event as a Unix timestamp in seconds; `0` when unavailable. + LastSourceTime Timestamp `json:"last_source_time" toon:"last_source_time"` + // Most recent received-event time as a Unix timestamp in seconds; `0` when no event has arrived. + LastTime Timestamp `json:"last_time" toon:"last_time"` + // Integration name. + Name string `json:"name" toon:"name"` + // Numeric integration type ID. Create uses the string `plugin_type` instead. + PluginID int64 `json:"plugin_id" toon:"plugin_id"` + // Integration type identifier, such as `standard.alert`. + PluginType string `json:"plugin_type" toon:"plugin_type"` + // Integration type display name, localized to the request language. + PluginTypeName string `json:"plugin_type_name" toon:"plugin_type_name"` + // Integration-type-specific configuration; saved secret values in responses are masked as `******`. + Settings map[string]any `json:"settings" toon:"settings"` + // Lifecycle status: `enabled` accepts alerts; `disabled` pauses ingestion. + Status string `json:"status" toon:"status"` + // Last update time as a Unix timestamp in seconds. + UpdatedAt Timestamp `json:"updated_at" toon:"updated_at"` +} + +// ChannelIntegrationListRequest is generated from the Flashduty OpenAPI schema. +type ChannelIntegrationListRequest struct { + ListOptions + // Channel ID to list dedicated integrations for. + ChannelID int64 `json:"channel_id" toon:"channel_id"` + // Filter by integration name. + Name string `json:"name,omitempty" toon:"name,omitempty"` + // Filter by one integration type. + PluginType string `json:"plugin_type,omitempty" toon:"plugin_type,omitempty"` + // Filter by `enabled` integrations that accept alerts or `disabled` integrations that pause ingestion. + Status string `json:"status,omitempty" toon:"status,omitempty"` +} + +// ChannelIntegrationListResponse is generated from the Flashduty OpenAPI schema. +type ChannelIntegrationListResponse struct { + ListOptions + // Dedicated integrations on this page. + Items []ChannelIntegrationItem `json:"items" toon:"items"` + // Total number of matching dedicated integrations. + Total int64 `json:"total" toon:"total"` +} + +// ChannelIntegrationUpdateRequest is generated from the Flashduty OpenAPI schema. +type ChannelIntegrationUpdateRequest struct { + // New description, at most 499 characters; empty clears it, omitted or null keeps it. + Description *string `json:"description,omitempty" toon:"description,omitempty"` + // Dedicated integration ID returned by create, list, or info. Public integration IDs are not accepted. + IntegrationID int64 `json:"integration_id" toon:"integration_id"` + // New name, 2–49 characters; omitted or null keeps the stored name. + Name *string `json:"name,omitempty" toon:"name,omitempty"` + // Replacement type-specific configuration. Omitted or null keeps it; masked or omitted secret fields retain their stored value. + Settings map[string]any `json:"settings,omitzero" toon:"settings,omitempty"` +} + // ChannelItem is generated from the Flashduty OpenAPI schema. type ChannelItem struct { // Owning account ID. @@ -8398,7 +8498,7 @@ type RoleUpsertResponse struct { // RotateIntegrationKeyResponse is generated from the Flashduty OpenAPI schema. type RotateIntegrationKeyResponse struct { - // New integration key; the previous key stops working immediately. Also readable from `/integration/list` and `/integration/info`. + // New integration key; the previous key stops working immediately. IntegrationKey string `json:"integration_key" toon:"integration_key"` } diff --git a/openapi/openapi.en.json b/openapi/openapi.en.json index 34eb8cb..443c609 100644 --- a/openapi/openapi.en.json +++ b/openapi/openapi.en.json @@ -33462,7 +33462,7 @@ "properties": { "integration_key": { "type": "string", - "description": "New integration key; the previous key stops working immediately. Also readable from `/integration/list` and `/integration/info`.", + "description": "New integration key; the previous key stops working immediately.", "example": "0123456789abcdef0123456789abcdef100" } } @@ -33600,6 +33600,317 @@ "required": [ "items" ] + }, + "ChannelIntegrationItem": { + "type": "object", + "description": "A channel-owned dedicated integration, with its complete key and masked settings.", + "required": [ + "integration_id", + "channel_id", + "integration_key", + "exclusive_data_source_id", + "plugin_id", + "plugin_type", + "plugin_type_name", + "category", + "name", + "description", + "status", + "created_at", + "updated_at", + "last_time", + "last_source_time", + "settings" + ], + "properties": { + "integration_id": { + "type": "integer", + "format": "int64", + "description": "Dedicated integration ID; use this ID for subsequent API calls." + }, + "channel_id": { + "type": "integer", + "format": "int64", + "description": "ID of the channel that owns the dedicated integration." + }, + "integration_key": { + "type": "string", + "description": "Complete integration key for alert ingestion; separate from the management App Key." + }, + "exclusive_data_source_id": { + "type": "integer", + "format": "int64", + "description": "Compatibility identifier for earlier integrations; `0` when absent. Use `integration_id` for this API group." + }, + "plugin_id": { + "type": "integer", + "format": "int64", + "description": "Numeric integration type ID. Create uses the string `plugin_type` instead." + }, + "plugin_type": { + "type": "string", + "description": "Integration type identifier, such as `standard.alert`." + }, + "plugin_type_name": { + "type": "string", + "description": "Integration type display name, localized to the request language." + }, + "category": { + "type": "string", + "description": "Integration category; dedicated integration creation accepts `event.alert` alert integrations." + }, + "name": { + "type": "string", + "description": "Integration name." + }, + "description": { + "type": "string", + "description": "Integration description; empty when unset." + }, + "status": { + "type": "string", + "enum": [ + "enabled", + "disabled" + ], + "description": "Lifecycle status: `enabled` accepts alerts; `disabled` pauses ingestion." + }, + "created_at": { + "type": "integer", + "format": "int64", + "description": "Creation time as a Unix timestamp in seconds." + }, + "updated_at": { + "type": "integer", + "format": "int64", + "description": "Last update time as a Unix timestamp in seconds." + }, + "last_time": { + "type": "integer", + "format": "int64", + "description": "Most recent received-event time as a Unix timestamp in seconds; `0` when no event has arrived." + }, + "last_source_time": { + "type": "integer", + "format": "int64", + "description": "Source time of the most recent event as a Unix timestamp in seconds; `0` when unavailable." + }, + "settings": { + "type": "object", + "additionalProperties": true, + "description": "Integration-type-specific configuration; saved secret values in responses are masked as `******`." + } + } + }, + "ChannelIntegrationIDRequest": { + "type": "object", + "description": "Identifies one dedicated integration.", + "required": [ + "integration_id" + ], + "properties": { + "integration_id": { + "type": "integer", + "format": "int64", + "minimum": 1, + "example": 10001, + "description": "Dedicated integration ID returned by create, list, or info. Public integration IDs are not accepted." + } + } + }, + "ChannelIntegrationListRequest": { + "type": "object", + "description": "Filters and pagination for one channel's dedicated integrations.", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "integer", + "format": "int64", + "minimum": 1, + "example": 20001, + "description": "Channel ID to list dedicated integrations for." + }, + "p": { + "type": "integer", + "minimum": 1, + "default": 1, + "description": "Page number, starting at 1." + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 100, + "description": "Number of items per page, at most 100." + }, + "name": { + "type": "string", + "maxLength": 49, + "description": "Filter by integration name." + }, + "plugin_type": { + "type": "string", + "description": "Filter by one integration type." + }, + "status": { + "type": "string", + "enum": [ + "enabled", + "disabled" + ], + "description": "Filter by `enabled` integrations that accept alerts or `disabled` integrations that pause ingestion." + } + } + }, + "ChannelIntegrationListResponse": { + "type": "object", + "description": "One page of dedicated integrations.", + "required": [ + "p", + "limit", + "total", + "items" + ], + "properties": { + "p": { + "type": "integer", + "description": "Returned page number." + }, + "limit": { + "type": "integer", + "description": "Returned page size." + }, + "total": { + "type": "integer", + "description": "Total number of matching dedicated integrations." + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChannelIntegrationItem" + }, + "description": "Dedicated integrations on this page." + } + } + }, + "ChannelIntegrationCreateRequest": { + "type": "object", + "description": "Creates a dedicated integration in an enabled channel.", + "required": [ + "channel_id", + "plugin_type" + ], + "properties": { + "channel_id": { + "type": "integer", + "format": "int64", + "minimum": 1, + "example": 20001, + "description": "Target channel ID; the channel must be enabled." + }, + "plugin_type": { + "type": "string", + "description": "Enabled alert integration type identifier, such as `standard.alert`." + }, + "name": { + "type": "string", + "maxLength": 49, + "description": "Integration name, 2–49 characters when supplied; omitted or empty uses the integration type default.", + "anyOf": [ + { + "maxLength": 0 + }, + { + "minLength": 2 + } + ] + }, + "description": { + "type": "string", + "maxLength": 499, + "description": "Description, at most 499 characters." + }, + "integration_key": { + "type": "string", + "maxLength": 35, + "description": "Optional supplied integration key. A non-empty value must have 35 characters, match the account suffix, and be unique.", + "anyOf": [ + { + "maxLength": 0 + }, + { + "minLength": 35 + } + ] + }, + "settings": { + "type": "object", + "additionalProperties": true, + "description": "Integration-type-specific configuration; saved secret values in responses are masked as `******`." + } + } + }, + "ChannelIntegrationCreateResponse": { + "type": "object", + "description": "The created dedicated integration and its ingestion key.", + "required": [ + "integration_id", + "integration_key" + ], + "properties": { + "integration_id": { + "type": "integer", + "format": "int64", + "description": "Created dedicated integration ID." + }, + "integration_key": { + "type": "string", + "description": "Integration key for alert ingestion." + } + } + }, + "ChannelIntegrationUpdateRequest": { + "type": "object", + "description": "Partial update of a dedicated integration; omitted or null fields keep their value.", + "required": [ + "integration_id" + ], + "properties": { + "integration_id": { + "type": "integer", + "format": "int64", + "minimum": 1, + "example": 10001, + "description": "Dedicated integration ID returned by create, list, or info. Public integration IDs are not accepted." + }, + "name": { + "type": [ + "string", + "null" + ], + "minLength": 2, + "maxLength": 49, + "description": "New name, 2–49 characters; omitted or null keeps the stored name." + }, + "description": { + "type": [ + "string", + "null" + ], + "maxLength": 499, + "description": "New description, at most 499 characters; empty clears it, omitted or null keeps it." + }, + "settings": { + "type": [ + "object", + "null" + ], + "additionalProperties": true, + "description": "Replacement type-specific configuration. Omitted or null keeps it; masked or omitted secret fields retain their stored value." + } + } } }, "securitySchemes": { @@ -67700,7 +68011,7 @@ "/integration/list": { "post": { "operationId": "integration-api-read-list", - "summary": "List integrations", + "summary": "List public integrations", "description": "List public integrations with their stored settings, decrypted keys, and edit permissions.", "tags": [ "On-call/Integrations" @@ -67805,7 +68116,7 @@ "/integration/info": { "post": { "operationId": "integration-api-read-info", - "summary": "Get integration detail", + "summary": "Get public integration detail", "description": "Get a public integration with its stored settings and decrypted integration key.", "tags": [ "On-call/Integrations" @@ -67898,7 +68209,7 @@ "/integration/create": { "post": { "operationId": "integration-api-write-create", - "summary": "Create integration", + "summary": "Create public integration", "description": "Create a public integration for a supported alert, change, IM, or webhook type.", "tags": [ "On-call/Integrations" @@ -67983,7 +68294,7 @@ "/integration/update": { "post": { "operationId": "integration-api-write-update", - "summary": "Update integration", + "summary": "Update public integration", "description": "Update an integration's name, description, team, or settings.", "tags": [ "On-call/Integrations" @@ -68087,7 +68398,7 @@ "/integration/delete": { "post": { "operationId": "integration-api-write-delete", - "summary": "Delete integration", + "summary": "Delete public integration", "description": "Delete an integration that nothing else references.", "tags": [ "On-call/Integrations" @@ -68144,29 +68455,670 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IntegrationLifecycleRequest" + "$ref": "#/components/schemas/IntegrationLifecycleRequest" + }, + "example": { + "integration_id": 6113996590131 + } + } + } + } + } + }, + "/integration/enable": { + "post": { + "operationId": "integration-api-write-enable", + "summary": "Enable public integration", + "description": "Re-enable a disabled integration so it accepts events again.", + "tags": [ + "On-call/Integrations" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Integrations Manage** (`on-call`) |\n\n## Usage\n\n- Enabling an integration resumes its scheduled pull task and heartbeat, and requires the integration type to be available on the current plan.\n- Every call is recorded in the account audit log.\n- These APIs manage public (account-level) integrations only, not channel-owned dedicated integrations.\n- Write access also requires object permission: the account owner, an administrator, the creator, or a member of the assigned team; with no team, members with Integrations Manage may operate. Read-only integrations cannot be changed.", + "href": "/en/api-reference/on-call/integrations/integration-api-write-enable", + "metadata": { + "sidebarTitle": "Enable integration" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/EmptyObject" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IntegrationLifecycleRequest" + }, + "example": { + "integration_id": 6113996590131 + } + } + } + } + } + }, + "/integration/disable": { + "post": { + "operationId": "integration-api-write-disable", + "summary": "Disable public integration", + "description": "Disable an integration without deleting its configuration.", + "tags": [ + "On-call/Integrations" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Integrations Manage** (`on-call`) |\n\n## Usage\n\n- Disabling stops event ingestion and pauses the scheduled pull task; settings are kept so the integration can be enabled again.\n- Every call is recorded in the account audit log.\n- These APIs manage public (account-level) integrations only, not channel-owned dedicated integrations.\n- Write access also requires object permission: the account owner, an administrator, the creator, or a member of the assigned team; with no team, members with Integrations Manage may operate. Read-only integrations cannot be changed.", + "href": "/en/api-reference/on-call/integrations/integration-api-write-disable", + "metadata": { + "sidebarTitle": "Disable integration" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/EmptyObject" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IntegrationLifecycleRequest" + }, + "example": { + "integration_id": 6113996590131 + } + } + } + } + } + }, + "/integration/key/rotate": { + "post": { + "operationId": "integration-api-write-rotate-key", + "summary": "Rotate public integration key", + "description": "Issue a new integration key and invalidate the previous one.", + "tags": [ + "On-call/Integrations" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Integrations Manage** (`on-call`) |\n\n## Usage\n\n- The previous key stops working immediately; after rotation, update the pushing side with the new key.\n- The new key can also be read from `/integration/list` and `/integration/info`.\n- These APIs manage public (account-level) integrations only, not channel-owned dedicated integrations.\n- Write access also requires object permission: the account owner, an administrator, the creator, or a member of the assigned team; with no team, members with Integrations Manage may operate. Read-only integrations cannot be changed.\n- Every call is recorded in the account audit log.", + "href": "/en/api-reference/on-call/integrations/integration-api-write-rotate-key", + "metadata": { + "sidebarTitle": "Rotate integration key" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/RotateIntegrationKeyResponse" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "integration_key": "0123456789abcdef0123456789abcdef100" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IntegrationLifecycleRequest" + }, + "example": { + "integration_id": 6113996590131 + } + } + } + } + } + }, + "/monit/folder/health": { + "post": { + "operationId": "monit-folder-read-health", + "summary": "Get monitor folder health", + "description": "Get health summaries for visible folders, including issues in their descendants.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **30 requests/minute**; **2 requests/second** per account |\n| Permissions | **Alerting Rules Read**, **Alerting Rules Manage**, **Node Permissions Read**, or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Issues include active alerts and alert-rule runtime problems; issues propagate to visible ancestors.\n- This operation scans folder rules and runtime state. Avoid frequent polling.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-read-health", + "metadata": { + "sidebarTitle": "Get monitor folder health" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderHealthOutput" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "folder_id": 12, + "has_issue": false + } + ] + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/monit/folder/create": { + "post": { + "operationId": "monit-folder-write-create", + "summary": "Create monitor folder", + "description": "Create a monitor folder at the root or under an accessible parent folder.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Use `parent_id: 0` or omit it to create a root folder. The parent must be accessible.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-create", + "metadata": { + "sidebarTitle": "Create monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout", + "note": "Checkout service folders", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderCreateRequest" + }, + "example": { + "name": "Checkout", + "note": "Checkout service folders", + "parent_id": 10, + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/update": { + "post": { + "operationId": "monit-folder-write-update", + "summary": "Update monitor folder", + "description": "Replace the name, note, and team assignment of an accessible monitor folder.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Send both `id` and `name`. This is a replacement of editable fields: omitting `note` clears it, and omitting `team_id` resets it to 0.\n- Use the move operation to change the parent folder.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-update", + "metadata": { + "sidebarTitle": "Update monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout production", + "note": "Production services", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderUpdateRequest" + }, + "example": { + "id": 12, + "name": "Checkout production", + "note": "Production services", + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/move": { + "post": { + "operationId": "monit-folder-write-move", + "summary": "Move monitor folder", + "description": "Move a monitor folder and its descendants under another folder or to the root.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- You must have access to both the source and destination folders.\n- `dest_parent_id` is required; send 0 explicitly to move to the root. Moving under the folder itself or a descendant is rejected.\n- Descendant paths are updated in the same transaction.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-move", + "metadata": { + "sidebarTitle": "Move monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Empty object." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderMoveRequest" + }, + "example": { + "id": 12, + "dest_parent_id": 0 + } + } + } + } + } + }, + "/monit/folder/delete": { + "post": { + "operationId": "monit-folder-write-delete", + "summary": "Delete monitor folder", + "description": "Delete an accessible monitor folder after removing its children, rules, and dashboards.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Only empty folders can be deleted. Child folders, alert rules, and dashboards block deletion with HTTP 409 and error code `FolderNotEmpty`.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-delete", + "metadata": { + "sidebarTitle": "Delete monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Empty object." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + }, + "409": { + "description": "Folder is not empty.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "description": "Request ID." + }, + "error": { + "type": "object", + "description": "Error details.", + "properties": { + "code": { + "type": "string", + "description": "FolderNotEmpty: remove the folder contents before retrying.", + "enum": [ + "FolderNotEmpty" + ] + }, + "message": { + "type": "string", + "description": "Error message." + } + }, + "required": [ + "code", + "message" + ] + } + }, + "required": [ + "request_id", + "error" + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "FolderNotEmpty", + "message": "Folder contains dashboards" + } + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderDeleteRequest" }, "example": { - "integration_id": 6113996590131 + "id": 12 } } } } } }, - "/integration/enable": { + "/channel/integration/list": { "post": { - "operationId": "integration-api-write-enable", - "summary": "Enable integration", - "description": "Re-enable a disabled integration so it accepts events again.", + "operationId": "channel-integration-read-list", + "summary": "List dedicated integrations", + "description": "List the dedicated integrations owned by one channel.", "tags": [ - "On-call/Integrations" + "On-call/Dedicated integrations" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Integrations Manage** (`on-call`) |\n\n## Usage\n\n- Enabling an integration resumes its scheduled pull task and heartbeat, and requires the integration type to be available on the current plan.\n- Every call is recorded in the account audit log.\n- These APIs manage public (account-level) integrations only, not channel-owned dedicated integrations.\n- Write access also requires object permission: the account owner, an administrator, the creator, or a member of the assigned team; with no team, members with Integrations Manage may operate. Read-only integrations cannot be changed.", - "href": "/en/api-reference/on-call/integrations/integration-api-write-enable", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | **Channels Read** or **Channels Manage** (`on-call`) |\n\n## Usage\n\n- These APIs manage channel-owned dedicated integrations; use `/integration/*` for public integrations.\n- The App Key's `api_scopes` must allow the operation; permissions follow the user who owns the key.\n- Private channels also require the account owner, an Admin, the channel creator, or membership in its owning or managing teams; public channels still require the API permission above.\n- `channel_id` is required; `p` defaults to `1` and `limit` defaults to `100`, with a maximum of `100`.\n- `plugin_type` and `status` each accept one value.\n- The complete `integration_key` is returned; saved secret values in `settings` are returned as `******`. The integration key authenticates alert ingestion and is separate from the management App Key.", + "href": "/en/api-reference/on-call/dedicated-integrations/channel-integration-read-list", "metadata": { - "sidebarTitle": "Enable integration" + "sidebarTitle": "List dedicated integrations" } }, "responses": { @@ -68183,7 +69135,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/EmptyObject" + "$ref": "#/components/schemas/ChannelIntegrationListResponse" } } } @@ -68191,7 +69143,31 @@ }, "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "data": {} + "data": { + "p": 1, + "limit": 20, + "total": 1, + "items": [ + { + "integration_id": 10001, + "channel_id": 20001, + "integration_key": "0123456789abcdef0123456789abcdef100", + "exclusive_data_source_id": 0, + "plugin_id": 9, + "plugin_type": "standard.alert", + "plugin_type_name": "Standard alert", + "category": "event.alert", + "name": "Payments alerts", + "description": "Alerts for the payments channel", + "status": "enabled", + "created_at": 1767225600, + "updated_at": 1767225600, + "last_time": 0, + "last_source_time": 0, + "settings": {} + } + ] + } } } } @@ -68202,6 +69178,9 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, @@ -68214,29 +69193,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IntegrationLifecycleRequest" + "$ref": "#/components/schemas/ChannelIntegrationListRequest" }, "example": { - "integration_id": 6113996590131 + "channel_id": 20001, + "p": 1, + "limit": 20 } } } } } }, - "/integration/disable": { + "/channel/integration/info": { "post": { - "operationId": "integration-api-write-disable", - "summary": "Disable integration", - "description": "Disable an integration without deleting its configuration.", + "operationId": "channel-integration-read-info", + "summary": "Get dedicated integration detail", + "description": "Get one channel-owned integration, including its integration key and masked settings.", "tags": [ - "On-call/Integrations" + "On-call/Dedicated integrations" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Integrations Manage** (`on-call`) |\n\n## Usage\n\n- Disabling stops event ingestion and pauses the scheduled pull task; settings are kept so the integration can be enabled again.\n- Every call is recorded in the account audit log.\n- These APIs manage public (account-level) integrations only, not channel-owned dedicated integrations.\n- Write access also requires object permission: the account owner, an administrator, the creator, or a member of the assigned team; with no team, members with Integrations Manage may operate. Read-only integrations cannot be changed.", - "href": "/en/api-reference/on-call/integrations/integration-api-write-disable", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | **Channels Read** or **Channels Manage** (`on-call`) |\n\n## Usage\n\n- These APIs manage channel-owned dedicated integrations; use `/integration/*` for public integrations.\n- The App Key's `api_scopes` must allow the operation; permissions follow the user who owns the key.\n- Private channels also require the account owner, an Admin, the channel creator, or membership in its owning or managing teams; public channels still require the API permission above.\n- The complete `integration_key` is returned; saved secret values in `settings` are returned as `******`. The integration key authenticates alert ingestion and is separate from the management App Key.", + "href": "/en/api-reference/on-call/dedicated-integrations/channel-integration-read-info", "metadata": { - "sidebarTitle": "Disable integration" + "sidebarTitle": "Get dedicated integration detail" } }, "responses": { @@ -68253,7 +69234,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/EmptyObject" + "$ref": "#/components/schemas/ChannelIntegrationItem" } } } @@ -68261,7 +69242,24 @@ }, "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "data": {} + "data": { + "integration_id": 10001, + "channel_id": 20001, + "integration_key": "0123456789abcdef0123456789abcdef100", + "exclusive_data_source_id": 0, + "plugin_id": 9, + "plugin_type": "standard.alert", + "plugin_type_name": "Standard alert", + "category": "event.alert", + "name": "Payments alerts", + "description": "Alerts for the payments channel", + "status": "enabled", + "created_at": 1767225600, + "updated_at": 1767225600, + "last_time": 0, + "last_source_time": 0, + "settings": {} + } } } } @@ -68272,6 +69270,9 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, @@ -68284,29 +69285,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IntegrationLifecycleRequest" + "$ref": "#/components/schemas/ChannelIntegrationIDRequest" }, "example": { - "integration_id": 6113996590131 + "integration_id": 10001 } } } } } }, - "/integration/key/rotate": { + "/channel/integration/create": { "post": { - "operationId": "integration-api-write-rotate-key", - "summary": "Rotate integration key", - "description": "Issue a new integration key and invalidate the previous one.", + "operationId": "channel-integration-write-create", + "summary": "Create dedicated integration", + "description": "Create a dedicated alert integration for an enabled channel.", "tags": [ - "On-call/Integrations" + "On-call/Dedicated integrations" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Integrations Manage** (`on-call`) |\n\n## Usage\n\n- The previous key stops working immediately; after rotation, update the pushing side with the new key.\n- The new key can also be read from `/integration/list` and `/integration/info`.\n- These APIs manage public (account-level) integrations only, not channel-owned dedicated integrations.\n- Write access also requires object permission: the account owner, an administrator, the creator, or a member of the assigned team; with no team, members with Integrations Manage may operate. Read-only integrations cannot be changed.\n- Every call is recorded in the account audit log.", - "href": "/en/api-reference/on-call/integrations/integration-api-write-rotate-key", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Channels Manage** (`on-call`) |\n\n## Usage\n\n- These APIs manage channel-owned dedicated integrations; use `/integration/*` for public integrations.\n- The App Key's `api_scopes` must allow the operation; permissions follow the user who owns the key.\n- You also need management access to the target channel: the account owner, an Admin, the channel creator, or a managing-team member. The owning team applies when no managing team is set.\n- Creating the integration does not independently grant channel management access; Integrations Manage does not grant access to this endpoint.\n- The channel must be enabled; the integration type must be enabled, belong to `event.alert`, and be supported by your subscription.\n- Query `POST /integration/type/list` and select a supported alert type for the dedicated integration.\n- An omitted `name` uses the integration type default; an omitted `integration_key` is generated. A supplied key must have 35 characters, match the account suffix, and not be used by another integration.\n- Success returns `integration_id` and `integration_key`; use that `integration_id` for subsequent operations.\n- Every call is recorded in the account audit log.\n- Concurrent changes to integrations in the same channel return `RequestLocked`; retry after the current change completes.", + "href": "/en/api-reference/on-call/dedicated-integrations/channel-integration-write-create", "metadata": { - "sidebarTitle": "Rotate integration key" + "sidebarTitle": "Create dedicated integration" } }, "responses": { @@ -68323,7 +69324,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/RotateIntegrationKeyResponse" + "$ref": "#/components/schemas/ChannelIntegrationCreateResponse" } } } @@ -68332,6 +69333,7 @@ "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", "data": { + "integration_id": 10001, "integration_key": "0123456789abcdef0123456789abcdef100" } } @@ -68344,11 +69346,31 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, "500": { "$ref": "#/components/responses/ServerError" + }, + "423": { + "description": "Another request is changing this channel's integrations. Retry after it completes.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "RequestLocked", + "message": "Channel integration is being changed, please try again" + } + } + } + } } }, "requestBody": { @@ -68356,29 +69378,33 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IntegrationLifecycleRequest" + "$ref": "#/components/schemas/ChannelIntegrationCreateRequest" }, "example": { - "integration_id": 6113996590131 + "channel_id": 20001, + "plugin_type": "standard.alert", + "name": "Payments alerts", + "description": "Alerts for the payments channel", + "settings": {} } } } } } }, - "/monit/folder/health": { + "/channel/integration/update": { "post": { - "operationId": "monit-folder-read-health", - "summary": "Get monitor folder health", - "description": "Get health summaries for visible folders, including issues in their descendants.", + "operationId": "channel-integration-write-update", + "summary": "Update dedicated integration", + "description": "Update the name, description, or settings of a dedicated integration.", "tags": [ - "Monitors/Rule folders" + "On-call/Dedicated integrations" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **30 requests/minute**; **2 requests/second** per account |\n| Permissions | **Alerting Rules Read**, **Alerting Rules Manage**, **Node Permissions Read**, or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Issues include active alerts and alert-rule runtime problems; issues propagate to visible ancestors.\n- This operation scans folder rules and runtime state. Avoid frequent polling.", - "href": "/en/api-reference/monitors/rule-folders/monit-folder-read-health", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Channels Manage** (`on-call`) |\n\n## Usage\n\n- These APIs manage channel-owned dedicated integrations; use `/integration/*` for public integrations.\n- The App Key's `api_scopes` must allow the operation; permissions follow the user who owns the key.\n- You also need management access to the target channel: the account owner, an Admin, the channel creator, or a managing-team member. The owning team applies when no managing team is set.\n- Creating the integration does not independently grant channel management access; Integrations Manage does not grant access to this endpoint.\n- The complete `integration_key` is returned; saved secret values in `settings` are returned as `******`. The integration key authenticates alert ingestion and is separate from the management App Key.\n- Only supplied fields change; omitted or `null` fields retain their value. Send `description: \"\"` to clear the description.\n- Supplied `settings` follow the integration type configuration rules; masked or omitted secret fields retain their stored value. Retaining a secret does not allow changing its corresponding destination.\n- This endpoint does not change the channel, integration type, or integration key.\n- Every call is recorded in the account audit log.\n- Concurrent changes to integrations in the same channel return `RequestLocked`; retry after the current change completes.", + "href": "/en/api-reference/on-call/dedicated-integrations/channel-integration-write-update", "metadata": { - "sidebarTitle": "Get monitor folder health" + "sidebarTitle": "Update dedicated integration" } }, "responses": { @@ -68395,7 +69421,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/FolderHealthOutput" + "$ref": "#/components/schemas/ChannelIntegrationItem" } } } @@ -68404,12 +69430,22 @@ "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", "data": { - "items": [ - { - "folder_id": 12, - "has_issue": false - } - ] + "integration_id": 10001, + "channel_id": 20001, + "integration_key": "0123456789abcdef0123456789abcdef100", + "exclusive_data_source_id": 0, + "plugin_id": 9, + "plugin_type": "standard.alert", + "plugin_type_name": "Standard alert", + "category": "event.alert", + "name": "Payments production alerts", + "description": "", + "status": "enabled", + "created_at": 1767225600, + "updated_at": 1767225600, + "last_time": 0, + "last_source_time": 0, + "settings": {} } } } @@ -68421,28 +69457,63 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, "500": { "$ref": "#/components/responses/ServerError" + }, + "423": { + "description": "Another request is changing this channel's integrations. Retry after it completes.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "RequestLocked", + "message": "Channel integration is being changed, please try again" + } + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChannelIntegrationUpdateRequest" + }, + "example": { + "integration_id": 10001, + "name": "Payments production alerts", + "description": "" + } + } } } } }, - "/monit/folder/create": { + "/channel/integration/delete": { "post": { - "operationId": "monit-folder-write-create", - "summary": "Create monitor folder", - "description": "Create a monitor folder at the root or under an accessible parent folder.", + "operationId": "channel-integration-write-delete", + "summary": "Delete dedicated integration", + "description": "Delete a dedicated integration after its references have been removed.", "tags": [ - "Monitors/Rule folders" + "On-call/Dedicated integrations" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Use `parent_id: 0` or omit it to create a root folder. The parent must be accessible.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", - "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-create", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Channels Manage** (`on-call`) |\n\n## Usage\n\n- These APIs manage channel-owned dedicated integrations; use `/integration/*` for public integrations.\n- The App Key's `api_scopes` must allow the operation; permissions follow the user who owns the key.\n- You also need management access to the target channel: the account owner, an Admin, the channel creator, or a managing-team member. The owning team applies when no managing team is set.\n- Creating the integration does not independently grant channel management access; Integrations Manage does not grant access to this endpoint.\n- Existing references return HTTP 400 with `ReferenceExist` and `data.refs` listing them; remove those references before deleting.\n- Every call is recorded in the account audit log.\n- Concurrent changes to integrations in the same channel return `RequestLocked`; retry after the current change completes.", + "href": "/en/api-reference/on-call/dedicated-integrations/channel-integration-write-delete", "metadata": { - "sidebarTitle": "Create monitor folder" + "sidebarTitle": "Delete dedicated integration" } }, "responses": { @@ -68459,7 +69530,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/FolderItem" + "$ref": "#/components/schemas/EmptyObject" } } } @@ -68467,36 +69538,112 @@ }, "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "data": { - "id": 12, - "account_id": 10023, - "parent_id": 10, - "parent_path": "10", - "team_id": 0, - "name": "Checkout", - "note": "Checkout service folders", - "creator_id": 10023, - "creator_name": "Ada Lovelace", - "updater_id": 10088, - "updater_name": "Grace Hopper", - "created_at": 1791000000, - "updated_at": 1791100800 - } + "data": {} } } } }, "400": { - "$ref": "#/components/responses/BadRequest" + "description": "Invalid parameters or an existing reference; ReferenceExist includes data.refs.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ErrorResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Resources that prevent deletion.", + "properties": { + "refs": { + "type": "array", + "description": "Referencing resources; remove their references before retrying.", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "Referencing resource kind." + }, + "name": { + "type": "string", + "description": "Referencing resource name." + }, + "href": { + "type": "string", + "description": "Console link to the referencing resource." + } + } + } + } + } + } + } + } + ] + }, + "examples": { + "missingParameter": { + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "InvalidParameter", + "message": "The specified parameter is not valid." + } + } + } + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "ReferenceExist", + "message": "There still are associated resources, deletion is blocked." + }, + "data": { + "refs": [ + { + "kind": "channel", + "name": "Payments", + "href": "https://console.flashcat.cloud/channel/20001" + } + ] + } + } + } + } }, "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, "500": { "$ref": "#/components/responses/ServerError" + }, + "423": { + "description": "Another request is changing this channel's integrations. Retry after it completes.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "RequestLocked", + "message": "Channel integration is being changed, please try again" + } + } + } + } } }, "requestBody": { @@ -68504,32 +69651,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FolderCreateRequest" + "$ref": "#/components/schemas/ChannelIntegrationIDRequest" }, "example": { - "name": "Checkout", - "note": "Checkout service folders", - "parent_id": 10, - "team_id": 0 + "integration_id": 10001 } } } } } }, - "/monit/folder/update": { + "/channel/integration/enable": { "post": { - "operationId": "monit-folder-write-update", - "summary": "Update monitor folder", - "description": "Replace the name, note, and team assignment of an accessible monitor folder.", + "operationId": "channel-integration-write-enable", + "summary": "Enable dedicated integration", + "description": "Enable a dedicated integration so it can receive alerts.", "tags": [ - "Monitors/Rule folders" + "On-call/Dedicated integrations" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Send both `id` and `name`. This is a replacement of editable fields: omitting `note` clears it, and omitting `team_id` resets it to 0.\n- Use the move operation to change the parent folder.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", - "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-update", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Channels Manage** (`on-call`) |\n\n## Usage\n\n- These APIs manage channel-owned dedicated integrations; use `/integration/*` for public integrations.\n- The App Key's `api_scopes` must allow the operation; permissions follow the user who owns the key.\n- You also need management access to the target channel: the account owner, an Admin, the channel creator, or a managing-team member. The owning team applies when no managing team is set.\n- Creating the integration does not independently grant channel management access; Integrations Manage does not grant access to this endpoint.\n- After permission and subscription checks pass, repeating an operation when the target state is already set succeeds.\n- Every call is recorded in the account audit log.\n- Concurrent changes to integrations in the same channel return `RequestLocked`; retry after the current change completes.", + "href": "/en/api-reference/on-call/dedicated-integrations/channel-integration-write-enable", "metadata": { - "sidebarTitle": "Update monitor folder" + "sidebarTitle": "Enable dedicated integration" } }, "responses": { @@ -68546,7 +69690,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/FolderItem" + "$ref": "#/components/schemas/EmptyObject" } } } @@ -68554,21 +69698,7 @@ }, "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "data": { - "id": 12, - "account_id": 10023, - "parent_id": 10, - "parent_path": "10", - "team_id": 0, - "name": "Checkout production", - "note": "Production services", - "creator_id": 10023, - "creator_name": "Ada Lovelace", - "updater_id": 10088, - "updater_name": "Grace Hopper", - "created_at": 1791000000, - "updated_at": 1791100800 - } + "data": {} } } } @@ -68579,11 +69709,31 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, "500": { "$ref": "#/components/responses/ServerError" + }, + "423": { + "description": "Another request is changing this channel's integrations. Retry after it completes.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "RequestLocked", + "message": "Channel integration is being changed, please try again" + } + } + } + } } }, "requestBody": { @@ -68591,32 +69741,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FolderUpdateRequest" + "$ref": "#/components/schemas/ChannelIntegrationIDRequest" }, "example": { - "id": 12, - "name": "Checkout production", - "note": "Production services", - "team_id": 0 + "integration_id": 10001 } } } } } }, - "/monit/folder/move": { + "/channel/integration/disable": { "post": { - "operationId": "monit-folder-write-move", - "summary": "Move monitor folder", - "description": "Move a monitor folder and its descendants under another folder or to the root.", + "operationId": "channel-integration-write-disable", + "summary": "Disable dedicated integration", + "description": "Disable a dedicated integration to pause alert ingestion.", "tags": [ - "Monitors/Rule folders" + "On-call/Dedicated integrations" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- You must have access to both the source and destination folders.\n- `dest_parent_id` is required; send 0 explicitly to move to the root. Moving under the folder itself or a descendant is rejected.\n- Descendant paths are updated in the same transaction.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", - "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-move", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Channels Manage** (`on-call`) |\n\n## Usage\n\n- These APIs manage channel-owned dedicated integrations; use `/integration/*` for public integrations.\n- The App Key's `api_scopes` must allow the operation; permissions follow the user who owns the key.\n- You also need management access to the target channel: the account owner, an Admin, the channel creator, or a managing-team member. The owning team applies when no managing team is set.\n- Creating the integration does not independently grant channel management access; Integrations Manage does not grant access to this endpoint.\n- After permission checks pass, repeating this operation when the integration is already disabled succeeds.\n- Every call is recorded in the account audit log.\n- Concurrent changes to integrations in the same channel return `RequestLocked`; retry after the current change completes.", + "href": "/en/api-reference/on-call/dedicated-integrations/channel-integration-write-disable", "metadata": { - "sidebarTitle": "Move monitor folder" + "sidebarTitle": "Disable dedicated integration" } }, "responses": { @@ -68633,8 +69780,7 @@ "type": "object", "properties": { "data": { - "type": "object", - "description": "Empty object." + "$ref": "#/components/schemas/EmptyObject" } } } @@ -68653,11 +69799,31 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, "500": { "$ref": "#/components/responses/ServerError" + }, + "423": { + "description": "Another request is changing this channel's integrations. Retry after it completes.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "RequestLocked", + "message": "Channel integration is being changed, please try again" + } + } + } + } } }, "requestBody": { @@ -68665,30 +69831,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FolderMoveRequest" + "$ref": "#/components/schemas/ChannelIntegrationIDRequest" }, "example": { - "id": 12, - "dest_parent_id": 0 + "integration_id": 10001 } } } } } }, - "/monit/folder/delete": { + "/channel/integration/key/rotate": { "post": { - "operationId": "monit-folder-write-delete", - "summary": "Delete monitor folder", - "description": "Delete an accessible monitor folder after removing its children, rules, and dashboards.", + "operationId": "channel-integration-write-rotate-key", + "summary": "Rotate dedicated integration key", + "description": "Issue a new dedicated integration key and invalidate the previous key.", "tags": [ - "Monitors/Rule folders" + "On-call/Dedicated integrations" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Only empty folders can be deleted. Child folders, alert rules, and dashboards block deletion with HTTP 409 and error code `FolderNotEmpty`.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", - "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-delete", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Channels Manage** (`on-call`) |\n\n## Usage\n\n- These APIs manage channel-owned dedicated integrations; use `/integration/*` for public integrations.\n- The App Key's `api_scopes` must allow the operation; permissions follow the user who owns the key.\n- You also need management access to the target channel: the account owner, an Admin, the channel creator, or a managing-team member. The owning team applies when no managing team is set.\n- Creating the integration does not independently grant channel management access; Integrations Manage does not grant access to this endpoint.\n- After the previous key is invalidated, senders using it can no longer ingest alerts; configure the returned key in the corresponding monitoring system.\n- Every call is recorded in the account audit log.\n- Concurrent changes to integrations in the same channel return `RequestLocked`; retry after the current change completes.", + "href": "/en/api-reference/on-call/dedicated-integrations/channel-integration-write-rotate-key", "metadata": { - "sidebarTitle": "Delete monitor folder" + "sidebarTitle": "Rotate dedicated integration key" } }, "responses": { @@ -68705,8 +69870,7 @@ "type": "object", "properties": { "data": { - "type": "object", - "description": "Empty object." + "$ref": "#/components/schemas/RotateIntegrationKeyResponse" } } } @@ -68714,7 +69878,9 @@ }, "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "data": {} + "data": { + "integration_key": "0123456789abcdef0123456789abcdef100" + } } } } @@ -68725,55 +69891,27 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, "500": { "$ref": "#/components/responses/ServerError" }, - "409": { - "description": "Folder is not empty.", + "423": { + "description": "Another request is changing this channel's integrations. Retry after it completes.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "request_id": { - "type": "string", - "description": "Request ID." - }, - "error": { - "type": "object", - "description": "Error details.", - "properties": { - "code": { - "type": "string", - "description": "FolderNotEmpty: remove the folder contents before retrying.", - "enum": [ - "FolderNotEmpty" - ] - }, - "message": { - "type": "string", - "description": "Error message." - } - }, - "required": [ - "code", - "message" - ] - } - }, - "required": [ - "request_id", - "error" - ] + "$ref": "#/components/schemas/ErrorResponse" }, "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", "error": { - "code": "FolderNotEmpty", - "message": "Folder contains dashboards" + "code": "RequestLocked", + "message": "Channel integration is being changed, please try again" } } } @@ -68785,10 +69923,10 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FolderDeleteRequest" + "$ref": "#/components/schemas/ChannelIntegrationIDRequest" }, "example": { - "id": 12 + "integration_id": 10001 } } } @@ -68965,6 +70103,10 @@ { "name": "Monitors/Rule folders", "description": "List the monitor folder tree that dashboards and alert rules are organised in." + }, + { + "name": "On-call/Dedicated integrations", + "description": "Manage alert integrations owned by individual channels." } ] } diff --git a/openapi/openapi.zh.json b/openapi/openapi.zh.json index 7401c4e..7c0bc39 100644 --- a/openapi/openapi.zh.json +++ b/openapi/openapi.zh.json @@ -33462,7 +33462,7 @@ "properties": { "integration_key": { "type": "string", - "description": "新的集成 Key,旧 Key 立即失效;新 Key 也可通过 `/integration/list` 和 `/integration/info` 查询。", + "description": "新的集成 Key;原 Key 立即失效。", "example": "0123456789abcdef0123456789abcdef100" } } @@ -33600,6 +33600,317 @@ "required": [ "items" ] + }, + "ChannelIntegrationItem": { + "type": "object", + "description": "协作空间专属集成,包含完整集成 Key 和脱敏配置。", + "required": [ + "integration_id", + "channel_id", + "integration_key", + "exclusive_data_source_id", + "plugin_id", + "plugin_type", + "plugin_type_name", + "category", + "name", + "description", + "status", + "created_at", + "updated_at", + "last_time", + "last_source_time", + "settings" + ], + "properties": { + "integration_id": { + "type": "integer", + "format": "int64", + "description": "专属集成 ID,后续接口调用使用此 ID。" + }, + "channel_id": { + "type": "integer", + "format": "int64", + "description": "专属集成所属的协作空间 ID。" + }, + "integration_key": { + "type": "string", + "description": "用于告警接入的完整集成 Key,与管理接口的 App Key 不同。" + }, + "exclusive_data_source_id": { + "type": "integer", + "format": "int64", + "description": "早期集成的兼容标识,无关联值时为 `0`;本组接口统一使用 `integration_id`。" + }, + "plugin_id": { + "type": "integer", + "format": "int64", + "description": "集成类型的数字 ID;创建时使用字符串 `plugin_type`。" + }, + "plugin_type": { + "type": "string", + "description": "集成类型标识,例如 `standard.alert`。" + }, + "plugin_type_name": { + "type": "string", + "description": "按请求语言返回的集成类型显示名称。" + }, + "category": { + "type": "string", + "description": "集成分类;专属集成创建接口接受 `event.alert` 告警集成。" + }, + "name": { + "type": "string", + "description": "集成名称。" + }, + "description": { + "type": "string", + "description": "集成描述;未设置时为空字符串。" + }, + "status": { + "type": "string", + "enum": [ + "enabled", + "disabled" + ], + "description": "集成状态:`enabled` 表示接收告警,`disabled` 表示暂停接入。" + }, + "created_at": { + "type": "integer", + "format": "int64", + "description": "创建时间,Unix 时间戳,单位为秒。" + }, + "updated_at": { + "type": "integer", + "format": "int64", + "description": "最近更新时间,Unix 时间戳,单位为秒。" + }, + "last_time": { + "type": "integer", + "format": "int64", + "description": "最近接收事件的时间,Unix 时间戳,单位为秒;未接收事件时为 `0`。" + }, + "last_source_time": { + "type": "integer", + "format": "int64", + "description": "最近事件的来源时间,Unix 时间戳,单位为秒;无可用值时为 `0`。" + }, + "settings": { + "type": "object", + "additionalProperties": true, + "description": "集成类型对应的配置;响应中已保存的密钥类值以 `******` 返回。" + } + } + }, + "ChannelIntegrationIDRequest": { + "type": "object", + "description": "标识一条专属集成。", + "required": [ + "integration_id" + ], + "properties": { + "integration_id": { + "type": "integer", + "format": "int64", + "minimum": 1, + "example": 10001, + "description": "创建、列表或详情接口返回的专属集成 ID;不接受公共集成 ID。" + } + } + }, + "ChannelIntegrationListRequest": { + "type": "object", + "description": "指定协作空间的专属集成查询条件。", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "integer", + "format": "int64", + "minimum": 1, + "example": 20001, + "description": "要查询专属集成的协作空间 ID。" + }, + "p": { + "type": "integer", + "minimum": 1, + "default": 1, + "description": "页码,从 1 开始。" + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 100, + "description": "每页条数,最多 100 条。" + }, + "name": { + "type": "string", + "maxLength": 49, + "description": "按集成名称过滤。" + }, + "plugin_type": { + "type": "string", + "description": "按单个集成类型过滤。" + }, + "status": { + "type": "string", + "enum": [ + "enabled", + "disabled" + ], + "description": "按状态过滤:`enabled` 表示接收告警,`disabled` 表示暂停接入。" + } + } + }, + "ChannelIntegrationListResponse": { + "type": "object", + "description": "一页专属集成结果。", + "required": [ + "p", + "limit", + "total", + "items" + ], + "properties": { + "p": { + "type": "integer", + "description": "当前页码。" + }, + "limit": { + "type": "integer", + "description": "当前每页条数。" + }, + "total": { + "type": "integer", + "description": "符合条件的专属集成总数。" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChannelIntegrationItem" + }, + "description": "当前页的专属集成。" + } + } + }, + "ChannelIntegrationCreateRequest": { + "type": "object", + "description": "在已启用的协作空间创建专属集成。", + "required": [ + "channel_id", + "plugin_type" + ], + "properties": { + "channel_id": { + "type": "integer", + "format": "int64", + "minimum": 1, + "example": 20001, + "description": "目标协作空间 ID,空间须已启用。" + }, + "plugin_type": { + "type": "string", + "description": "已启用的告警集成类型标识,例如 `standard.alert`。" + }, + "name": { + "type": "string", + "maxLength": 49, + "description": "集成名称,提供时为 2–49 个字符;省略或为空时使用集成类型的默认名称。", + "anyOf": [ + { + "maxLength": 0 + }, + { + "minLength": 2 + } + ] + }, + "description": { + "type": "string", + "maxLength": 499, + "description": "集成描述,最多 499 个字符。" + }, + "integration_key": { + "type": "string", + "maxLength": 35, + "description": "可选的自带集成 Key;非空时须为 35 个字符、匹配账户后缀,且未被其他集成使用。", + "anyOf": [ + { + "maxLength": 0 + }, + { + "minLength": 35 + } + ] + }, + "settings": { + "type": "object", + "additionalProperties": true, + "description": "集成类型对应的配置;响应中已保存的密钥类值以 `******` 返回。" + } + } + }, + "ChannelIntegrationCreateResponse": { + "type": "object", + "description": "创建完成的专属集成及其接入 Key。", + "required": [ + "integration_id", + "integration_key" + ], + "properties": { + "integration_id": { + "type": "integer", + "format": "int64", + "description": "创建完成的专属集成 ID。" + }, + "integration_key": { + "type": "string", + "description": "用于告警接入的集成 Key。" + } + } + }, + "ChannelIntegrationUpdateRequest": { + "type": "object", + "description": "专属集成的部分更新;省略或为 null 的字段保留原值。", + "required": [ + "integration_id" + ], + "properties": { + "integration_id": { + "type": "integer", + "format": "int64", + "minimum": 1, + "example": 10001, + "description": "创建、列表或详情接口返回的专属集成 ID;不接受公共集成 ID。" + }, + "name": { + "type": [ + "string", + "null" + ], + "minLength": 2, + "maxLength": 49, + "description": "新名称,2–49 个字符;省略或为 null 时保留原名称。" + }, + "description": { + "type": [ + "string", + "null" + ], + "maxLength": 499, + "description": "新描述,最多 499 个字符;空字符串清空,省略或为 null 时保留。" + }, + "settings": { + "type": [ + "object", + "null" + ], + "additionalProperties": true, + "description": "按集成类型替换配置;省略或为 null 时保留,已脱敏或省略的密钥类字段保留原值。" + } + } } }, "securitySchemes": { @@ -67700,7 +68011,7 @@ "/integration/list": { "post": { "operationId": "integration-api-read-list", - "summary": "查询集成列表", + "summary": "查询公共集成列表", "description": "查询公共集成列表,返回存储配置、解密 Key 与当前调用者的编辑权限。", "tags": [ "On-call/集成中心" @@ -67805,7 +68116,7 @@ "/integration/info": { "post": { "operationId": "integration-api-read-info", - "summary": "查询集成详情", + "summary": "查看公共集成详情", "description": "查询单个公共集成详情,返回存储配置及解密后的集成 Key。", "tags": [ "On-call/集成中心" @@ -67898,7 +68209,7 @@ "/integration/create": { "post": { "operationId": "integration-api-write-create", - "summary": "创建集成", + "summary": "创建公共集成", "description": "创建支持的告警源、变更源、IM 或 Webhook 类型公共集成。", "tags": [ "On-call/集成中心" @@ -67983,7 +68294,7 @@ "/integration/update": { "post": { "operationId": "integration-api-write-update", - "summary": "更新集成", + "summary": "更新公共集成", "description": "更新集成的名称、描述、所属团队或配置。", "tags": [ "On-call/集成中心" @@ -68087,7 +68398,7 @@ "/integration/delete": { "post": { "operationId": "integration-api-write-delete", - "summary": "删除集成", + "summary": "删除公共集成", "description": "删除指定的集成。", "tags": [ "On-call/集成中心" @@ -68144,29 +68455,670 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IntegrationLifecycleRequest" + "$ref": "#/components/schemas/IntegrationLifecycleRequest" + }, + "example": { + "integration_id": 6113996590131 + } + } + } + } + } + }, + "/integration/enable": { + "post": { + "operationId": "integration-api-write-enable", + "summary": "启用公共集成", + "description": "重新启用已禁用的集成,恢复接收事件。", + "tags": [ + "On-call/集成中心" + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **集成中心管理**(`on-call`) |\n\n## 使用说明\n\n- 启用会恢复该集成的拉取任务与心跳,并要求当前套餐包含该集成类型。\n- 每次调用都会记录到账户审计日志。\n- 这些接口仅管理公共(账户级)集成,不管理协作空间专属集成。\n- 写操作还需对象权限:主体账户、管理员、创建者或所属团队成员可操作;未设置团队时,拥有集成中心管理权限的成员可操作,只读集成不可修改。", + "href": "/zh/api-reference/on-call/integrations/integration-api-write-enable", + "metadata": { + "sidebarTitle": "启用集成" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/EmptyObject" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IntegrationLifecycleRequest" + }, + "example": { + "integration_id": 6113996590131 + } + } + } + } + } + }, + "/integration/disable": { + "post": { + "operationId": "integration-api-write-disable", + "summary": "禁用公共集成", + "description": "禁用集成,但保留其配置。", + "tags": [ + "On-call/集成中心" + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **集成中心管理**(`on-call`) |\n\n## 使用说明\n\n- 禁用后停止接收事件并暂停定时拉取任务,配置保留,可再次启用。\n- 每次调用都会记录到账户审计日志。\n- 这些接口仅管理公共(账户级)集成,不管理协作空间专属集成。\n- 写操作还需对象权限:主体账户、管理员、创建者或所属团队成员可操作;未设置团队时,拥有集成中心管理权限的成员可操作,只读集成不可修改。", + "href": "/zh/api-reference/on-call/integrations/integration-api-write-disable", + "metadata": { + "sidebarTitle": "禁用集成" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/EmptyObject" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IntegrationLifecycleRequest" + }, + "example": { + "integration_id": 6113996590131 + } + } + } + } + } + }, + "/integration/key/rotate": { + "post": { + "operationId": "integration-api-write-rotate-key", + "summary": "轮换公共集成 Key", + "description": "生成新的集成 Key,并使旧 Key 立即失效。", + "tags": [ + "On-call/集成中心" + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **集成中心管理**(`on-call`) |\n\n## 使用说明\n\n- 旧 Key 会立即失效;轮换后请将推送端配置更新为新 Key。\n- 新 Key 也可通过 `/integration/list` 和 `/integration/info` 查询。\n- 这些接口仅管理公共(账户级)集成,不管理协作空间专属集成。\n- 写操作还需对象权限:主体账户、管理员、创建者或所属团队成员可操作;未设置团队时,拥有集成中心管理权限的成员可操作,只读集成不可修改。\n- 每次调用都会记录到账户审计日志。", + "href": "/zh/api-reference/on-call/integrations/integration-api-write-rotate-key", + "metadata": { + "sidebarTitle": "轮换集成 Key" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/RotateIntegrationKeyResponse" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "integration_key": "0123456789abcdef0123456789abcdef100" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IntegrationLifecycleRequest" + }, + "example": { + "integration_id": 6113996590131 + } + } + } + } + } + }, + "/monit/folder/health": { + "post": { + "operationId": "monit-folder-read-health", + "summary": "获取监控目录健康摘要", + "description": "获取可见目录的健康摘要,包含后代目录中的问题。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **30 次/分钟**;**2 次/秒** |\n| 权限 | **告警规则查看**、**告警规则管理**、**节点权限查看**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 问题包括活跃告警和告警规则运行问题,并向可见祖先目录汇总。\n- 此操作扫描目录规则和运行状态,请避免频繁轮询。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-read-health", + "metadata": { + "sidebarTitle": "获取监控目录健康摘要" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderHealthOutput" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "folder_id": 12, + "has_issue": false + } + ] + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/monit/folder/create": { + "post": { + "operationId": "monit-folder-write-create", + "summary": "创建监控目录", + "description": "在顶层或有权访问的父目录下创建监控目录。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 传入 `parent_id: 0` 或省略该字段以创建顶层目录。您必须有权访问父目录。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-create", + "metadata": { + "sidebarTitle": "创建监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout", + "note": "Checkout service folders", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderCreateRequest" + }, + "example": { + "name": "Checkout", + "note": "Checkout service folders", + "parent_id": 10, + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/update": { + "post": { + "operationId": "monit-folder-write-update", + "summary": "更新监控目录", + "description": "替换有权访问的监控目录的名称、备注和团队归属。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 必须传入 `id` 和 `name`。此操作替换可编辑字段:省略 `note` 会清空备注,省略 `team_id` 会重置为 0。\n- 请使用移动接口更改父目录。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-update", + "metadata": { + "sidebarTitle": "更新监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout production", + "note": "Production services", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderUpdateRequest" + }, + "example": { + "id": 12, + "name": "Checkout production", + "note": "Production services", + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/move": { + "post": { + "operationId": "monit-folder-write-move", + "summary": "移动监控目录", + "description": "将监控目录及其后代移至其他目录下或顶层。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 您必须有权访问源目录和目标目录。\n- 必须传入 `dest_parent_id`;移至顶层时显式传 0。不能移至自身或后代目录下。\n- 后代目录路径在同一事务中更新。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-move", + "metadata": { + "sidebarTitle": "移动监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "空对象。" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderMoveRequest" + }, + "example": { + "id": 12, + "dest_parent_id": 0 + } + } + } + } + } + }, + "/monit/folder/delete": { + "post": { + "operationId": "monit-folder-write-delete", + "summary": "删除监控目录", + "description": "移除子目录、规则和仪表盘后,删除有权访问的监控目录。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 只能删除空目录。存在子目录、告警规则或仪表盘时,返回 HTTP 409,错误码为 `FolderNotEmpty`。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-delete", + "metadata": { + "sidebarTitle": "删除监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "空对象。" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + }, + "409": { + "description": "目录非空。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "description": "请求 ID。" + }, + "error": { + "type": "object", + "description": "错误详情。", + "properties": { + "code": { + "type": "string", + "description": "FolderNotEmpty:请移除目录内容后重试。", + "enum": [ + "FolderNotEmpty" + ] + }, + "message": { + "type": "string", + "description": "错误消息。" + } + }, + "required": [ + "code", + "message" + ] + } + }, + "required": [ + "request_id", + "error" + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "FolderNotEmpty", + "message": "Folder contains dashboards" + } + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderDeleteRequest" }, "example": { - "integration_id": 6113996590131 + "id": 12 } } } } } }, - "/integration/enable": { + "/channel/integration/list": { "post": { - "operationId": "integration-api-write-enable", - "summary": "启用集成", - "description": "重新启用已禁用的集成,恢复接收事件。", + "operationId": "channel-integration-read-list", + "summary": "查询专属集成列表", + "description": "查询指定协作空间的专属集成。", "tags": [ - "On-call/集成中心" + "On-call/专属集成" ], "x-mint": { - "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **集成中心管理**(`on-call`) |\n\n## 使用说明\n\n- 启用会恢复该集成的拉取任务与心跳,并要求当前套餐包含该集成类型。\n- 每次调用都会记录到账户审计日志。\n- 这些接口仅管理公共(账户级)集成,不管理协作空间专属集成。\n- 写操作还需对象权限:主体账户、管理员、创建者或所属团队成员可操作;未设置团队时,拥有集成中心管理权限的成员可操作,只读集成不可修改。", - "href": "/zh/api-reference/on-call/integrations/integration-api-write-enable", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **1,000 次/分钟**;**50 次/秒** |\n| 权限要求 | **协作空间查看**或**协作空间管理**(`on-call`) |\n\n## 使用说明\n\n- 这些接口管理协作空间专属集成;公共集成使用 `/integration/*`。\n- App Key 的接口授权范围(`api_scopes`)须允许本次操作;权限按 App Key 所属用户判断。\n- 查看私有空间还要求您是账户所有者、Admin、空间创建者,或所属团队、管理团队的成员;公开空间仍需上述接口权限。\n- `channel_id` 必填;`p` 默认 `1`,`limit` 默认 `100`,最多 `100`。\n- `plugin_type` 和 `status` 各接受单个值。\n- 返回完整 `integration_key`;`settings` 中已保存的密钥类值返回 `******`。集成 Key 用于告警接入,与管理接口的 App Key 不同。", + "href": "/zh/api-reference/on-call/dedicated-integrations/channel-integration-read-list", "metadata": { - "sidebarTitle": "启用集成" + "sidebarTitle": "查询专属集成列表" } }, "responses": { @@ -68183,7 +69135,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/EmptyObject" + "$ref": "#/components/schemas/ChannelIntegrationListResponse" } } } @@ -68191,7 +69143,31 @@ }, "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "data": {} + "data": { + "p": 1, + "limit": 20, + "total": 1, + "items": [ + { + "integration_id": 10001, + "channel_id": 20001, + "integration_key": "0123456789abcdef0123456789abcdef100", + "exclusive_data_source_id": 0, + "plugin_id": 9, + "plugin_type": "standard.alert", + "plugin_type_name": "Standard alert", + "category": "event.alert", + "name": "Payments alerts", + "description": "Alerts for the payments channel", + "status": "enabled", + "created_at": 1767225600, + "updated_at": 1767225600, + "last_time": 0, + "last_source_time": 0, + "settings": {} + } + ] + } } } } @@ -68202,6 +69178,9 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, @@ -68214,29 +69193,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IntegrationLifecycleRequest" + "$ref": "#/components/schemas/ChannelIntegrationListRequest" }, "example": { - "integration_id": 6113996590131 + "channel_id": 20001, + "p": 1, + "limit": 20 } } } } } }, - "/integration/disable": { + "/channel/integration/info": { "post": { - "operationId": "integration-api-write-disable", - "summary": "禁用集成", - "description": "禁用集成,但保留其配置。", + "operationId": "channel-integration-read-info", + "summary": "查看专属集成详情", + "description": "查看专属集成详情、集成 Key 和脱敏配置。", "tags": [ - "On-call/集成中心" + "On-call/专属集成" ], "x-mint": { - "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **集成中心管理**(`on-call`) |\n\n## 使用说明\n\n- 禁用后停止接收事件并暂停定时拉取任务,配置保留,可再次启用。\n- 每次调用都会记录到账户审计日志。\n- 这些接口仅管理公共(账户级)集成,不管理协作空间专属集成。\n- 写操作还需对象权限:主体账户、管理员、创建者或所属团队成员可操作;未设置团队时,拥有集成中心管理权限的成员可操作,只读集成不可修改。", - "href": "/zh/api-reference/on-call/integrations/integration-api-write-disable", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **1,000 次/分钟**;**50 次/秒** |\n| 权限要求 | **协作空间查看**或**协作空间管理**(`on-call`) |\n\n## 使用说明\n\n- 这些接口管理协作空间专属集成;公共集成使用 `/integration/*`。\n- App Key 的接口授权范围(`api_scopes`)须允许本次操作;权限按 App Key 所属用户判断。\n- 查看私有空间还要求您是账户所有者、Admin、空间创建者,或所属团队、管理团队的成员;公开空间仍需上述接口权限。\n- 返回完整 `integration_key`;`settings` 中已保存的密钥类值返回 `******`。集成 Key 用于告警接入,与管理接口的 App Key 不同。", + "href": "/zh/api-reference/on-call/dedicated-integrations/channel-integration-read-info", "metadata": { - "sidebarTitle": "禁用集成" + "sidebarTitle": "查看专属集成详情" } }, "responses": { @@ -68253,7 +69234,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/EmptyObject" + "$ref": "#/components/schemas/ChannelIntegrationItem" } } } @@ -68261,7 +69242,24 @@ }, "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "data": {} + "data": { + "integration_id": 10001, + "channel_id": 20001, + "integration_key": "0123456789abcdef0123456789abcdef100", + "exclusive_data_source_id": 0, + "plugin_id": 9, + "plugin_type": "standard.alert", + "plugin_type_name": "Standard alert", + "category": "event.alert", + "name": "Payments alerts", + "description": "Alerts for the payments channel", + "status": "enabled", + "created_at": 1767225600, + "updated_at": 1767225600, + "last_time": 0, + "last_source_time": 0, + "settings": {} + } } } } @@ -68272,6 +69270,9 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, @@ -68284,29 +69285,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IntegrationLifecycleRequest" + "$ref": "#/components/schemas/ChannelIntegrationIDRequest" }, "example": { - "integration_id": 6113996590131 + "integration_id": 10001 } } } } } }, - "/integration/key/rotate": { + "/channel/integration/create": { "post": { - "operationId": "integration-api-write-rotate-key", - "summary": "轮换集成 Key", - "description": "生成新的集成 Key,并使旧 Key 立即失效。", + "operationId": "channel-integration-write-create", + "summary": "创建专属集成", + "description": "为已启用的协作空间创建专属告警集成。", "tags": [ - "On-call/集成中心" + "On-call/专属集成" ], "x-mint": { - "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **集成中心管理**(`on-call`) |\n\n## 使用说明\n\n- 旧 Key 会立即失效;轮换后请将推送端配置更新为新 Key。\n- 新 Key 也可通过 `/integration/list` 和 `/integration/info` 查询。\n- 这些接口仅管理公共(账户级)集成,不管理协作空间专属集成。\n- 写操作还需对象权限:主体账户、管理员、创建者或所属团队成员可操作;未设置团队时,拥有集成中心管理权限的成员可操作,只读集成不可修改。\n- 每次调用都会记录到账户审计日志。", - "href": "/zh/api-reference/on-call/integrations/integration-api-write-rotate-key", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **协作空间管理**(`on-call`) |\n\n## 使用说明\n\n- 这些接口管理协作空间专属集成;公共集成使用 `/integration/*`。\n- App Key 的接口授权范围(`api_scopes`)须允许本次操作;权限按 App Key 所属用户判断。\n- 您还须拥有目标空间的管理权限:账户所有者、Admin、空间创建者,或管理团队成员。未设置管理团队时,按所属团队判断。\n- 创建过这条集成不单独授予空间管理权限;集成中心管理权限也不授予本接口访问权。\n- 目标空间必须已启用;集成类型须已启用、属于 `event.alert`,且您的订阅支持该类型。\n- 通过 `POST /integration/type/list` 查询类型;为专属集成选择支持的告警类型。\n- 省略 `name` 时使用集成类型的默认名称;省略 `integration_key` 时自动生成。自带 Key 须为 35 个字符、匹配账户后缀,且未被其他集成使用。\n- 成功后返回 `integration_id` 和 `integration_key`;后续操作使用返回的 `integration_id`。\n- 每次调用均记录到账户审计日志。\n- 同一空间的集成正在变更时返回 `RequestLocked`,请稍后重试。", + "href": "/zh/api-reference/on-call/dedicated-integrations/channel-integration-write-create", "metadata": { - "sidebarTitle": "轮换集成 Key" + "sidebarTitle": "创建专属集成" } }, "responses": { @@ -68323,7 +69324,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/RotateIntegrationKeyResponse" + "$ref": "#/components/schemas/ChannelIntegrationCreateResponse" } } } @@ -68332,6 +69333,7 @@ "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", "data": { + "integration_id": 10001, "integration_key": "0123456789abcdef0123456789abcdef100" } } @@ -68344,11 +69346,31 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, "500": { "$ref": "#/components/responses/ServerError" + }, + "423": { + "description": "同一空间的集成正在变更,请稍后重试。", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "RequestLocked", + "message": "Channel integration is being changed, please try again" + } + } + } + } } }, "requestBody": { @@ -68356,29 +69378,33 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IntegrationLifecycleRequest" + "$ref": "#/components/schemas/ChannelIntegrationCreateRequest" }, "example": { - "integration_id": 6113996590131 + "channel_id": 20001, + "plugin_type": "standard.alert", + "name": "Payments alerts", + "description": "Alerts for the payments channel", + "settings": {} } } } } } }, - "/monit/folder/health": { + "/channel/integration/update": { "post": { - "operationId": "monit-folder-read-health", - "summary": "获取监控目录健康摘要", - "description": "获取可见目录的健康摘要,包含后代目录中的问题。", + "operationId": "channel-integration-write-update", + "summary": "更新专属集成", + "description": "更新专属集成的名称、描述或配置。", "tags": [ - "Monitors/规则分组" + "On-call/专属集成" ], "x-mint": { - "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **30 次/分钟**;**2 次/秒** |\n| 权限 | **告警规则查看**、**告警规则管理**、**节点权限查看**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 问题包括活跃告警和告警规则运行问题,并向可见祖先目录汇总。\n- 此操作扫描目录规则和运行状态,请避免频繁轮询。", - "href": "/zh/api-reference/monitors/rule-folders/monit-folder-read-health", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **协作空间管理**(`on-call`) |\n\n## 使用说明\n\n- 这些接口管理协作空间专属集成;公共集成使用 `/integration/*`。\n- App Key 的接口授权范围(`api_scopes`)须允许本次操作;权限按 App Key 所属用户判断。\n- 您还须拥有目标空间的管理权限:账户所有者、Admin、空间创建者,或管理团队成员。未设置管理团队时,按所属团队判断。\n- 创建过这条集成不单独授予空间管理权限;集成中心管理权限也不授予本接口访问权。\n- 返回完整 `integration_key`;`settings` 中已保存的密钥类值返回 `******`。集成 Key 用于告警接入,与管理接口的 App Key 不同。\n- 仅更新传入的字段;省略或设为 `null` 时保留原值。`description: \"\"` 清空描述。\n- 传入 `settings` 时按该类型的配置规则保存;已脱敏或省略的密钥类字段保留原值。保留原密钥时,不能同时更改对应的目标地址。\n- 此接口不更改所属空间、集成类型或集成 Key。\n- 每次调用均记录到账户审计日志。\n- 同一空间的集成正在变更时返回 `RequestLocked`,请稍后重试。", + "href": "/zh/api-reference/on-call/dedicated-integrations/channel-integration-write-update", "metadata": { - "sidebarTitle": "获取监控目录健康摘要" + "sidebarTitle": "更新专属集成" } }, "responses": { @@ -68395,7 +69421,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/FolderHealthOutput" + "$ref": "#/components/schemas/ChannelIntegrationItem" } } } @@ -68404,12 +69430,22 @@ "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", "data": { - "items": [ - { - "folder_id": 12, - "has_issue": false - } - ] + "integration_id": 10001, + "channel_id": 20001, + "integration_key": "0123456789abcdef0123456789abcdef100", + "exclusive_data_source_id": 0, + "plugin_id": 9, + "plugin_type": "standard.alert", + "plugin_type_name": "Standard alert", + "category": "event.alert", + "name": "Payments production alerts", + "description": "", + "status": "enabled", + "created_at": 1767225600, + "updated_at": 1767225600, + "last_time": 0, + "last_source_time": 0, + "settings": {} } } } @@ -68421,28 +69457,63 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, "500": { "$ref": "#/components/responses/ServerError" + }, + "423": { + "description": "同一空间的集成正在变更,请稍后重试。", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "RequestLocked", + "message": "Channel integration is being changed, please try again" + } + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChannelIntegrationUpdateRequest" + }, + "example": { + "integration_id": 10001, + "name": "Payments production alerts", + "description": "" + } + } } } } }, - "/monit/folder/create": { + "/channel/integration/delete": { "post": { - "operationId": "monit-folder-write-create", - "summary": "创建监控目录", - "description": "在顶层或有权访问的父目录下创建监控目录。", + "operationId": "channel-integration-write-delete", + "summary": "删除专属集成", + "description": "删除已解除资源引用的专属集成。", "tags": [ - "Monitors/规则分组" + "On-call/专属集成" ], "x-mint": { - "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 传入 `parent_id: 0` 或省略该字段以创建顶层目录。您必须有权访问父目录。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", - "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-create", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **协作空间管理**(`on-call`) |\n\n## 使用说明\n\n- 这些接口管理协作空间专属集成;公共集成使用 `/integration/*`。\n- App Key 的接口授权范围(`api_scopes`)须允许本次操作;权限按 App Key 所属用户判断。\n- 您还须拥有目标空间的管理权限:账户所有者、Admin、空间创建者,或管理团队成员。未设置管理团队时,按所属团队判断。\n- 创建过这条集成不单独授予空间管理权限;集成中心管理权限也不授予本接口访问权。\n- 存在其他资源引用时,返回 HTTP 400、`ReferenceExist`,并在 `data.refs` 中列出引用;解除引用后再删除。\n- 每次调用均记录到账户审计日志。\n- 同一空间的集成正在变更时返回 `RequestLocked`,请稍后重试。", + "href": "/zh/api-reference/on-call/dedicated-integrations/channel-integration-write-delete", "metadata": { - "sidebarTitle": "创建监控目录" + "sidebarTitle": "删除专属集成" } }, "responses": { @@ -68459,7 +69530,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/FolderItem" + "$ref": "#/components/schemas/EmptyObject" } } } @@ -68467,36 +69538,112 @@ }, "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "data": { - "id": 12, - "account_id": 10023, - "parent_id": 10, - "parent_path": "10", - "team_id": 0, - "name": "Checkout", - "note": "Checkout service folders", - "creator_id": 10023, - "creator_name": "Ada Lovelace", - "updater_id": 10088, - "updater_name": "Grace Hopper", - "created_at": 1791000000, - "updated_at": 1791100800 - } + "data": {} } } } }, "400": { - "$ref": "#/components/responses/BadRequest" + "description": "参数无效或存在资源引用;ReferenceExist 错误包含 data.refs。", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ErrorResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "阻止删除的引用资源。", + "properties": { + "refs": { + "type": "array", + "description": "引用当前集成的资源;解除引用后再重试。", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "引用资源的类型。" + }, + "name": { + "type": "string", + "description": "引用资源的名称。" + }, + "href": { + "type": "string", + "description": "引用资源的控制台链接。" + } + } + } + } + } + } + } + } + ] + }, + "examples": { + "missingParameter": { + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "InvalidParameter", + "message": "The specified parameter is not valid." + } + } + } + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "ReferenceExist", + "message": "There still are associated resources, deletion is blocked." + }, + "data": { + "refs": [ + { + "kind": "channel", + "name": "Payments", + "href": "https://console.flashcat.cloud/channel/20001" + } + ] + } + } + } + } }, "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, "500": { "$ref": "#/components/responses/ServerError" + }, + "423": { + "description": "同一空间的集成正在变更,请稍后重试。", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "RequestLocked", + "message": "Channel integration is being changed, please try again" + } + } + } + } } }, "requestBody": { @@ -68504,32 +69651,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FolderCreateRequest" + "$ref": "#/components/schemas/ChannelIntegrationIDRequest" }, "example": { - "name": "Checkout", - "note": "Checkout service folders", - "parent_id": 10, - "team_id": 0 + "integration_id": 10001 } } } } } }, - "/monit/folder/update": { + "/channel/integration/enable": { "post": { - "operationId": "monit-folder-write-update", - "summary": "更新监控目录", - "description": "替换有权访问的监控目录的名称、备注和团队归属。", + "operationId": "channel-integration-write-enable", + "summary": "启用专属集成", + "description": "启用专属集成,使其接收告警。", "tags": [ - "Monitors/规则分组" + "On-call/专属集成" ], "x-mint": { - "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 必须传入 `id` 和 `name`。此操作替换可编辑字段:省略 `note` 会清空备注,省略 `team_id` 会重置为 0。\n- 请使用移动接口更改父目录。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", - "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-update", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **协作空间管理**(`on-call`) |\n\n## 使用说明\n\n- 这些接口管理协作空间专属集成;公共集成使用 `/integration/*`。\n- App Key 的接口授权范围(`api_scopes`)须允许本次操作;权限按 App Key 所属用户判断。\n- 您还须拥有目标空间的管理权限:账户所有者、Admin、空间创建者,或管理团队成员。未设置管理团队时,按所属团队判断。\n- 创建过这条集成不单独授予空间管理权限;集成中心管理权限也不授予本接口访问权。\n- 权限及订阅校验通过后,已处于目标状态时重复调用成功返回。\n- 每次调用均记录到账户审计日志。\n- 同一空间的集成正在变更时返回 `RequestLocked`,请稍后重试。", + "href": "/zh/api-reference/on-call/dedicated-integrations/channel-integration-write-enable", "metadata": { - "sidebarTitle": "更新监控目录" + "sidebarTitle": "启用专属集成" } }, "responses": { @@ -68546,7 +69690,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/FolderItem" + "$ref": "#/components/schemas/EmptyObject" } } } @@ -68554,21 +69698,7 @@ }, "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "data": { - "id": 12, - "account_id": 10023, - "parent_id": 10, - "parent_path": "10", - "team_id": 0, - "name": "Checkout production", - "note": "Production services", - "creator_id": 10023, - "creator_name": "Ada Lovelace", - "updater_id": 10088, - "updater_name": "Grace Hopper", - "created_at": 1791000000, - "updated_at": 1791100800 - } + "data": {} } } } @@ -68579,11 +69709,31 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, "500": { "$ref": "#/components/responses/ServerError" + }, + "423": { + "description": "同一空间的集成正在变更,请稍后重试。", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "RequestLocked", + "message": "Channel integration is being changed, please try again" + } + } + } + } } }, "requestBody": { @@ -68591,32 +69741,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FolderUpdateRequest" + "$ref": "#/components/schemas/ChannelIntegrationIDRequest" }, "example": { - "id": 12, - "name": "Checkout production", - "note": "Production services", - "team_id": 0 + "integration_id": 10001 } } } } } }, - "/monit/folder/move": { + "/channel/integration/disable": { "post": { - "operationId": "monit-folder-write-move", - "summary": "移动监控目录", - "description": "将监控目录及其后代移至其他目录下或顶层。", + "operationId": "channel-integration-write-disable", + "summary": "禁用专属集成", + "description": "禁用专属集成,暂停接收告警。", "tags": [ - "Monitors/规则分组" + "On-call/专属集成" ], "x-mint": { - "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 您必须有权访问源目录和目标目录。\n- 必须传入 `dest_parent_id`;移至顶层时显式传 0。不能移至自身或后代目录下。\n- 后代目录路径在同一事务中更新。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", - "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-move", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **协作空间管理**(`on-call`) |\n\n## 使用说明\n\n- 这些接口管理协作空间专属集成;公共集成使用 `/integration/*`。\n- App Key 的接口授权范围(`api_scopes`)须允许本次操作;权限按 App Key 所属用户判断。\n- 您还须拥有目标空间的管理权限:账户所有者、Admin、空间创建者,或管理团队成员。未设置管理团队时,按所属团队判断。\n- 创建过这条集成不单独授予空间管理权限;集成中心管理权限也不授予本接口访问权。\n- 权限校验通过后,已处于禁用状态时重复调用成功返回。\n- 每次调用均记录到账户审计日志。\n- 同一空间的集成正在变更时返回 `RequestLocked`,请稍后重试。", + "href": "/zh/api-reference/on-call/dedicated-integrations/channel-integration-write-disable", "metadata": { - "sidebarTitle": "移动监控目录" + "sidebarTitle": "禁用专属集成" } }, "responses": { @@ -68633,8 +69780,7 @@ "type": "object", "properties": { "data": { - "type": "object", - "description": "空对象。" + "$ref": "#/components/schemas/EmptyObject" } } } @@ -68653,11 +69799,31 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, "500": { "$ref": "#/components/responses/ServerError" + }, + "423": { + "description": "同一空间的集成正在变更,请稍后重试。", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "RequestLocked", + "message": "Channel integration is being changed, please try again" + } + } + } + } } }, "requestBody": { @@ -68665,30 +69831,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FolderMoveRequest" + "$ref": "#/components/schemas/ChannelIntegrationIDRequest" }, "example": { - "id": 12, - "dest_parent_id": 0 + "integration_id": 10001 } } } } } }, - "/monit/folder/delete": { + "/channel/integration/key/rotate": { "post": { - "operationId": "monit-folder-write-delete", - "summary": "删除监控目录", - "description": "移除子目录、规则和仪表盘后,删除有权访问的监控目录。", + "operationId": "channel-integration-write-rotate-key", + "summary": "轮换专属集成 Key", + "description": "生成新的专属集成 Key,并使原 Key 失效。", "tags": [ - "Monitors/规则分组" + "On-call/专属集成" ], "x-mint": { - "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 只能删除空目录。存在子目录、告警规则或仪表盘时,返回 HTTP 409,错误码为 `FolderNotEmpty`。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", - "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-delete", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **协作空间管理**(`on-call`) |\n\n## 使用说明\n\n- 这些接口管理协作空间专属集成;公共集成使用 `/integration/*`。\n- App Key 的接口授权范围(`api_scopes`)须允许本次操作;权限按 App Key 所属用户判断。\n- 您还须拥有目标空间的管理权限:账户所有者、Admin、空间创建者,或管理团队成员。未设置管理团队时,按所属团队判断。\n- 创建过这条集成不单独授予空间管理权限;集成中心管理权限也不授予本接口访问权。\n- 原 Key 失效后,仍使用原 Key 的推送端将无法接入;请将返回的新 Key 配置到对应监控系统。\n- 每次调用均记录到账户审计日志。\n- 同一空间的集成正在变更时返回 `RequestLocked`,请稍后重试。", + "href": "/zh/api-reference/on-call/dedicated-integrations/channel-integration-write-rotate-key", "metadata": { - "sidebarTitle": "删除监控目录" + "sidebarTitle": "轮换专属集成 Key" } }, "responses": { @@ -68705,8 +69870,7 @@ "type": "object", "properties": { "data": { - "type": "object", - "description": "空对象。" + "$ref": "#/components/schemas/RotateIntegrationKeyResponse" } } } @@ -68714,7 +69878,9 @@ }, "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "data": {} + "data": { + "integration_key": "0123456789abcdef0123456789abcdef100" + } } } } @@ -68725,55 +69891,27 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, "500": { "$ref": "#/components/responses/ServerError" }, - "409": { - "description": "目录非空。", + "423": { + "description": "同一空间的集成正在变更,请稍后重试。", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "request_id": { - "type": "string", - "description": "请求 ID。" - }, - "error": { - "type": "object", - "description": "错误详情。", - "properties": { - "code": { - "type": "string", - "description": "FolderNotEmpty:请移除目录内容后重试。", - "enum": [ - "FolderNotEmpty" - ] - }, - "message": { - "type": "string", - "description": "错误消息。" - } - }, - "required": [ - "code", - "message" - ] - } - }, - "required": [ - "request_id", - "error" - ] + "$ref": "#/components/schemas/ErrorResponse" }, "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", "error": { - "code": "FolderNotEmpty", - "message": "Folder contains dashboards" + "code": "RequestLocked", + "message": "Channel integration is being changed, please try again" } } } @@ -68785,10 +69923,10 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FolderDeleteRequest" + "$ref": "#/components/schemas/ChannelIntegrationIDRequest" }, "example": { - "id": 12 + "integration_id": 10001 } } } @@ -68965,6 +70103,10 @@ { "name": "Monitors/规则分组", "description": "列出仪表盘与告警规则所在的监控节点树。" + }, + { + "name": "On-call/专属集成", + "description": "管理各协作空间的专属告警集成。" } ] } diff --git a/roundtrip_gen_test.go b/roundtrip_gen_test.go index ea4cdb8..6af8e76 100644 --- a/roundtrip_gen_test.go +++ b/roundtrip_gen_test.go @@ -43,6 +43,11 @@ var exampleDataDecoders = map[string]func(json.RawMessage) error{ "POST /channel/infos": func(d json.RawMessage) error { var v ChannelInfosResponse; return json.Unmarshal(d, &v) }, "POST /channel/inhibit/rule/create": func(d json.RawMessage) error { var v RuleCreateResponse; return json.Unmarshal(d, &v) }, "POST /channel/inhibit/rule/list": func(d json.RawMessage) error { var v ListInhibitRulesResponse; return json.Unmarshal(d, &v) }, + "POST /channel/integration/create": func(d json.RawMessage) error { var v ChannelIntegrationCreateResponse; return json.Unmarshal(d, &v) }, + "POST /channel/integration/info": func(d json.RawMessage) error { var v ChannelIntegrationItem; return json.Unmarshal(d, &v) }, + "POST /channel/integration/key/rotate": func(d json.RawMessage) error { var v RotateIntegrationKeyResponse; return json.Unmarshal(d, &v) }, + "POST /channel/integration/list": func(d json.RawMessage) error { var v ChannelIntegrationListResponse; return json.Unmarshal(d, &v) }, + "POST /channel/integration/update": func(d json.RawMessage) error { var v ChannelIntegrationItem; return json.Unmarshal(d, &v) }, "POST /channel/list": func(d json.RawMessage) error { var v ListChannelsResponse; return json.Unmarshal(d, &v) }, "POST /channel/silence/rule/create": func(d json.RawMessage) error { var v RuleCreateResponse; return json.Unmarshal(d, &v) }, "POST /channel/silence/rule/list": func(d json.RawMessage) error { var v ListSilenceRulesResponse; return json.Unmarshal(d, &v) }, diff --git a/services_gen.go b/services_gen.go index bd101e1..3c77878 100644 --- a/services_gen.go +++ b/services_gen.go @@ -26,6 +26,7 @@ type genServices struct { Calendars *CalendarsService Changes *ChangesService Channels *ChannelsService + DedicatedIntegrations *DedicatedIntegrationsService ImIntegrations *ImIntegrationsService Incidents *IncidentsService Integrations *IntegrationsService @@ -70,6 +71,7 @@ func (c *Client) initServices() { c.Calendars = (*CalendarsService)(&c.common) c.Changes = (*ChangesService)(&c.common) c.Channels = (*ChannelsService)(&c.common) + c.DedicatedIntegrations = (*DedicatedIntegrationsService)(&c.common) c.ImIntegrations = (*ImIntegrationsService)(&c.common) c.Incidents = (*IncidentsService)(&c.common) c.Integrations = (*IntegrationsService)(&c.common) From 3be69ba34a82c76348c94911258ada193142507c Mon Sep 17 00:00:00 2001 From: alick-liming Date: Tue, 27 Oct 2026 12:46:28 +0800 Subject: [PATCH 2/2] docs: make dedicated integration type lookup optional --- openapi/openapi.en.json | 2 +- openapi/openapi.zh.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openapi/openapi.en.json b/openapi/openapi.en.json index 443c609..59ab2e5 100644 --- a/openapi/openapi.en.json +++ b/openapi/openapi.en.json @@ -69304,7 +69304,7 @@ "On-call/Dedicated integrations" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Channels Manage** (`on-call`) |\n\n## Usage\n\n- These APIs manage channel-owned dedicated integrations; use `/integration/*` for public integrations.\n- The App Key's `api_scopes` must allow the operation; permissions follow the user who owns the key.\n- You also need management access to the target channel: the account owner, an Admin, the channel creator, or a managing-team member. The owning team applies when no managing team is set.\n- Creating the integration does not independently grant channel management access; Integrations Manage does not grant access to this endpoint.\n- The channel must be enabled; the integration type must be enabled, belong to `event.alert`, and be supported by your subscription.\n- Query `POST /integration/type/list` and select a supported alert type for the dedicated integration.\n- An omitted `name` uses the integration type default; an omitted `integration_key` is generated. A supplied key must have 35 characters, match the account suffix, and not be used by another integration.\n- Success returns `integration_id` and `integration_key`; use that `integration_id` for subsequent operations.\n- Every call is recorded in the account audit log.\n- Concurrent changes to integrations in the same channel return `RequestLocked`; retry after the current change completes.", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Channels Manage** (`on-call`) |\n\n## Usage\n\n- These APIs manage channel-owned dedicated integrations; use `/integration/*` for public integrations.\n- The App Key's `api_scopes` must allow the operation; permissions follow the user who owns the key.\n- You also need management access to the target channel: the account owner, an Admin, the channel creator, or a managing-team member. The owning team applies when no managing team is set.\n- Creating the integration does not independently grant channel management access; Integrations Manage does not grant access to this endpoint.\n- The channel must be enabled; the integration type must be enabled, belong to `event.alert`, and be supported by your subscription.\n- If you know the `plugin_type`, create the integration directly without querying types first. You can optionally query `POST /integration/type/list` for supported alert types if you have **Integrations Read** permission.\n- An omitted `name` uses the integration type default; an omitted `integration_key` is generated. A supplied key must have 35 characters, match the account suffix, and not be used by another integration.\n- Success returns `integration_id` and `integration_key`; use that `integration_id` for subsequent operations.\n- Every call is recorded in the account audit log.\n- Concurrent changes to integrations in the same channel return `RequestLocked`; retry after the current change completes.", "href": "/en/api-reference/on-call/dedicated-integrations/channel-integration-write-create", "metadata": { "sidebarTitle": "Create dedicated integration" diff --git a/openapi/openapi.zh.json b/openapi/openapi.zh.json index 7c0bc39..b74efe9 100644 --- a/openapi/openapi.zh.json +++ b/openapi/openapi.zh.json @@ -69304,7 +69304,7 @@ "On-call/专属集成" ], "x-mint": { - "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **协作空间管理**(`on-call`) |\n\n## 使用说明\n\n- 这些接口管理协作空间专属集成;公共集成使用 `/integration/*`。\n- App Key 的接口授权范围(`api_scopes`)须允许本次操作;权限按 App Key 所属用户判断。\n- 您还须拥有目标空间的管理权限:账户所有者、Admin、空间创建者,或管理团队成员。未设置管理团队时,按所属团队判断。\n- 创建过这条集成不单独授予空间管理权限;集成中心管理权限也不授予本接口访问权。\n- 目标空间必须已启用;集成类型须已启用、属于 `event.alert`,且您的订阅支持该类型。\n- 通过 `POST /integration/type/list` 查询类型;为专属集成选择支持的告警类型。\n- 省略 `name` 时使用集成类型的默认名称;省略 `integration_key` 时自动生成。自带 Key 须为 35 个字符、匹配账户后缀,且未被其他集成使用。\n- 成功后返回 `integration_id` 和 `integration_key`;后续操作使用返回的 `integration_id`。\n- 每次调用均记录到账户审计日志。\n- 同一空间的集成正在变更时返回 `RequestLocked`,请稍后重试。", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个账户 **300 次/分钟**;**20 次/秒** |\n| 权限要求 | **协作空间管理**(`on-call`) |\n\n## 使用说明\n\n- 这些接口管理协作空间专属集成;公共集成使用 `/integration/*`。\n- App Key 的接口授权范围(`api_scopes`)须允许本次操作;权限按 App Key 所属用户判断。\n- 您还须拥有目标空间的管理权限:账户所有者、Admin、空间创建者,或管理团队成员。未设置管理团队时,按所属团队判断。\n- 创建过这条集成不单独授予空间管理权限;集成中心管理权限也不授予本接口访问权。\n- 目标空间必须已启用;集成类型须已启用、属于 `event.alert`,且您的订阅支持该类型。\n- 已知 `plugin_type` 时可直接创建,无需先查询类型。拥有**集成中心查看**权限时,可选用 `POST /integration/type/list` 查询支持的告警类型。\n- 省略 `name` 时使用集成类型的默认名称;省略 `integration_key` 时自动生成。自带 Key 须为 35 个字符、匹配账户后缀,且未被其他集成使用。\n- 成功后返回 `integration_id` 和 `integration_key`;后续操作使用返回的 `integration_id`。\n- 每次调用均记录到账户审计日志。\n- 同一空间的集成正在变更时返回 `RequestLocked`,请稍后重试。", "href": "/zh/api-reference/on-call/dedicated-integrations/channel-integration-write-create", "metadata": { "sidebarTitle": "创建专属集成"