From b4d6335b542d9c92356952319292425f101efb18 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Thu, 2 Apr 2026 10:32:05 -0700 Subject: [PATCH 1/8] feat(rootly): add Rootly incident management integration with 14 tools --- apps/docs/components/icons.tsx | 36 + apps/docs/components/ui/icon-mapping.ts | 2 + apps/docs/content/docs/en/tools/meta.json | 1 + apps/docs/content/docs/en/tools/rippling.mdx | 4 +- apps/docs/content/docs/en/tools/rootly.mdx | 499 +++++++++++ .../integrations/data/icon-mapping.ts | 2 + .../integrations/data/integrations.json | 79 +- apps/sim/blocks/blocks/rootly.ts | 829 ++++++++++++++++++ apps/sim/blocks/registry.ts | 2 + apps/sim/components/icons.tsx | 36 + apps/sim/tools/registry.ts | 30 + apps/sim/tools/rootly/add_incident_event.ts | 111 +++ apps/sim/tools/rootly/create_alert.ts | 159 ++++ apps/sim/tools/rootly/create_incident.ts | 208 +++++ apps/sim/tools/rootly/get_incident.ts | 97 ++ apps/sim/tools/rootly/index.ts | 14 + apps/sim/tools/rootly/list_alerts.ts | 142 +++ apps/sim/tools/rootly/list_environments.ts | 114 +++ apps/sim/tools/rootly/list_functionalities.ts | 114 +++ apps/sim/tools/rootly/list_incident_types.ts | 114 +++ apps/sim/tools/rootly/list_incidents.ts | 179 ++++ apps/sim/tools/rootly/list_retrospectives.ts | 125 +++ apps/sim/tools/rootly/list_services.ts | 111 +++ apps/sim/tools/rootly/list_severities.ts | 108 +++ apps/sim/tools/rootly/list_teams.ts | 108 +++ apps/sim/tools/rootly/types.ts | 357 ++++++++ apps/sim/tools/rootly/update_incident.ts | 176 ++++ 27 files changed, 3753 insertions(+), 4 deletions(-) create mode 100644 apps/docs/content/docs/en/tools/rootly.mdx create mode 100644 apps/sim/blocks/blocks/rootly.ts create mode 100644 apps/sim/tools/rootly/add_incident_event.ts create mode 100644 apps/sim/tools/rootly/create_alert.ts create mode 100644 apps/sim/tools/rootly/create_incident.ts create mode 100644 apps/sim/tools/rootly/get_incident.ts create mode 100644 apps/sim/tools/rootly/index.ts create mode 100644 apps/sim/tools/rootly/list_alerts.ts create mode 100644 apps/sim/tools/rootly/list_environments.ts create mode 100644 apps/sim/tools/rootly/list_functionalities.ts create mode 100644 apps/sim/tools/rootly/list_incident_types.ts create mode 100644 apps/sim/tools/rootly/list_incidents.ts create mode 100644 apps/sim/tools/rootly/list_retrospectives.ts create mode 100644 apps/sim/tools/rootly/list_services.ts create mode 100644 apps/sim/tools/rootly/list_severities.ts create mode 100644 apps/sim/tools/rootly/list_teams.ts create mode 100644 apps/sim/tools/rootly/types.ts create mode 100644 apps/sim/tools/rootly/update_incident.ts diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 6614c9f6036..35cf31a2ce4 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -6387,6 +6387,42 @@ export function RipplingIcon(props: SVGProps) { ) } +export function RootlyIcon(props: SVGProps) { + return ( + + + + + + + + + + + ) +} + export function HexIcon(props: SVGProps) { return ( diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index ef92dc123f3..b8edc84d2bf 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -139,6 +139,7 @@ import { ResendIcon, RevenueCatIcon, RipplingIcon, + RootlyIcon, S3Icon, SalesforceIcon, SearchIcon, @@ -320,6 +321,7 @@ export const blockTypeToIconMap: Record = { resend: ResendIcon, revenuecat: RevenueCatIcon, rippling: RipplingIcon, + rootly: RootlyIcon, s3: S3Icon, salesforce: SalesforceIcon, search: SearchIcon, diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index 20dd3a4bf9f..6071d7c7a73 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -134,6 +134,7 @@ "resend", "revenuecat", "rippling", + "rootly", "s3", "salesforce", "search", diff --git a/apps/docs/content/docs/en/tools/rippling.mdx b/apps/docs/content/docs/en/tools/rippling.mdx index 31945d94aca..88160e37d23 100644 --- a/apps/docs/content/docs/en/tools/rippling.mdx +++ b/apps/docs/content/docs/en/tools/rippling.mdx @@ -2201,7 +2201,7 @@ Create a new object category ### `rippling_update_object_category` -Update a object category +Update an object category #### Input @@ -2224,7 +2224,7 @@ Update a object category ### `rippling_delete_object_category` -Delete a object category +Delete an object category #### Input diff --git a/apps/docs/content/docs/en/tools/rootly.mdx b/apps/docs/content/docs/en/tools/rootly.mdx new file mode 100644 index 00000000000..8288a1dee45 --- /dev/null +++ b/apps/docs/content/docs/en/tools/rootly.mdx @@ -0,0 +1,499 @@ +--- +title: Rootly +description: Manage incidents, alerts, and on-call with Rootly +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[Rootly](https://rootly.com/) is an incident management platform that helps teams respond to, mitigate, and learn from incidents — all without leaving Slack or your existing tools. Rootly automates on-call alerting, incident workflows, status page updates, and retrospectives so engineering teams can resolve issues faster and reduce toil. + +**Why Rootly?** +- **End-to-End Incident Management:** Create, track, update, and resolve incidents with full lifecycle support — from initial triage through retrospective. +- **On-Call Alerting:** Create and manage alerts with deduplication, routing, and escalation to ensure the right people are notified immediately. +- **Timeline Events:** Add structured timeline events to incidents for clear, auditable incident narratives. +- **Service Catalog:** Maintain a catalog of services and map them to incidents for precise impact tracking. +- **Severity & Prioritization:** Use configurable severity levels to prioritize incidents and drive appropriate response urgency. +- **Retrospectives:** Access post-incident retrospectives to identify root causes, capture learnings, and drive reliability improvements. + +**Using Rootly in Sim** + +Sim's Rootly integration connects your agentic workflows directly to your Rootly account using an API key. With operations spanning incidents, alerts, services, severities, teams, environments, functionalities, incident types, and retrospectives, you can build powerful incident management automations without writing backend code. + +**Key benefits of using Rootly in Sim:** +- **Automated incident creation:** Trigger incident creation from monitoring alerts, customer reports, or anomaly detection workflows with full metadata including severity, services, and teams. +- **Incident lifecycle automation:** Automatically update incident status, add timeline events, and attach mitigation or resolution messages as your response progresses. +- **Alert management:** Create and list alerts with deduplication support to integrate Rootly into your existing monitoring and notification pipelines. +- **Organizational awareness:** Query services, severities, teams, environments, functionalities, and incident types to build context-aware incident workflows. +- **Retrospective insights:** List and filter retrospectives to feed post-incident learnings into continuous improvement workflows. + +Whether you're automating incident response, building on-call alerting pipelines, or driving post-incident learning, Rootly in Sim gives you direct, secure access to the Rootly API — no middleware required. Simply configure your API key, select the operation you need, and let Sim handle the rest. +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +Integrate Rootly incident management into workflows. Create and manage incidents, alerts, services, severities, and retrospectives. + + + +## Tools + +### `rootly_create_incident` + +Create a new incident in Rootly with optional severity, services, and teams. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `title` | string | Yes | The title of the incident | +| `summary` | string | No | A summary of the incident | +| `severityId` | string | No | Severity ID to attach to the incident | +| `status` | string | No | Incident status \(in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled\) | +| `kind` | string | No | Incident kind \(normal, test, example, backfilled, scheduled\) | +| `serviceIds` | string | No | Comma-separated service IDs to attach | +| `environmentIds` | string | No | Comma-separated environment IDs to attach | +| `groupIds` | string | No | Comma-separated team/group IDs to attach | +| `incidentTypeIds` | string | No | Comma-separated incident type IDs to attach | +| `functionalityIds` | string | No | Comma-separated functionality IDs to attach | +| `labels` | string | No | Labels as JSON object, e.g. \{"platform":"osx","version":"1.29"\} | +| `private` | boolean | No | Create as a private incident \(cannot be undone\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `incident` | object | The created incident | +| ↳ `id` | string | Unique incident ID | +| ↳ `sequentialId` | number | Sequential incident number | +| ↳ `title` | string | Incident title | +| ↳ `slug` | string | Incident slug | +| ↳ `kind` | string | Incident kind | +| ↳ `summary` | string | Incident summary | +| ↳ `status` | string | Incident status | +| ↳ `private` | boolean | Whether the incident is private | +| ↳ `url` | string | URL to the incident | +| ↳ `shortUrl` | string | Short URL to the incident | +| ↳ `severityName` | string | Severity name | +| ↳ `severityId` | string | Severity ID | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| ↳ `startedAt` | string | Start date | +| ↳ `mitigatedAt` | string | Mitigation date | +| ↳ `resolvedAt` | string | Resolution date | +| ↳ `closedAt` | string | Closed date | + +### `rootly_get_incident` + +Retrieve a single incident by ID from Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `incidentId` | string | Yes | The ID of the incident to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `incident` | object | The incident details | +| ↳ `id` | string | Unique incident ID | +| ↳ `sequentialId` | number | Sequential incident number | +| ↳ `title` | string | Incident title | +| ↳ `slug` | string | Incident slug | +| ↳ `kind` | string | Incident kind | +| ↳ `summary` | string | Incident summary | +| ↳ `status` | string | Incident status | +| ↳ `url` | string | URL to the incident | +| ↳ `severityName` | string | Severity name | +| ↳ `severityId` | string | Severity ID | +| ↳ `startedAt` | string | Start date | +| ↳ `mitigatedAt` | string | Mitigation date | +| ↳ `resolvedAt` | string | Resolution date | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | + +### `rootly_update_incident` + +Update an existing incident in Rootly (status, severity, summary, etc.). + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `incidentId` | string | Yes | The ID of the incident to update | +| `title` | string | No | Updated incident title | +| `summary` | string | No | Updated incident summary | +| `severityId` | string | No | Updated severity ID | +| `status` | string | No | Updated status \(in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled\) | +| `serviceIds` | string | No | Comma-separated service IDs | +| `environmentIds` | string | No | Comma-separated environment IDs | +| `groupIds` | string | No | Comma-separated team/group IDs | +| `mitigationMessage` | string | No | How was the incident mitigated? | +| `resolutionMessage` | string | No | How was the incident resolved? | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `incident` | object | The updated incident | +| ↳ `id` | string | Unique incident ID | +| ↳ `sequentialId` | number | Sequential incident number | +| ↳ `title` | string | Incident title | +| ↳ `slug` | string | Incident slug | +| ↳ `kind` | string | Incident kind | +| ↳ `summary` | string | Incident summary | +| ↳ `status` | string | Incident status | +| ↳ `private` | boolean | Whether the incident is private | +| ↳ `url` | string | URL to the incident | +| ↳ `shortUrl` | string | Short URL to the incident | +| ↳ `severityName` | string | Severity name | +| ↳ `severityId` | string | Severity ID | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| ↳ `startedAt` | string | Start date | +| ↳ `mitigatedAt` | string | Mitigation date | +| ↳ `resolvedAt` | string | Resolution date | +| ↳ `closedAt` | string | Closed date | + +### `rootly_list_incidents` + +List incidents from Rootly with optional filtering by status, severity, and more. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `status` | string | No | Filter by status \(in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled\) | +| `severity` | string | No | Filter by severity slug | +| `search` | string | No | Search term to filter incidents | +| `services` | string | No | Filter by service slugs \(comma-separated\) | +| `teams` | string | No | Filter by team slugs \(comma-separated\) | +| `environments` | string | No | Filter by environment slugs \(comma-separated\) | +| `sort` | string | No | Sort order \(e.g., -created_at, created_at, -started_at\) | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `incidents` | array | List of incidents | +| ↳ `id` | string | Unique incident ID | +| ↳ `sequentialId` | number | Sequential incident number | +| ↳ `title` | string | Incident title | +| ↳ `slug` | string | Incident slug | +| ↳ `kind` | string | Incident kind | +| ↳ `summary` | string | Incident summary | +| ↳ `status` | string | Incident status | +| ↳ `private` | boolean | Whether the incident is private | +| ↳ `url` | string | URL to the incident | +| ↳ `shortUrl` | string | Short URL to the incident | +| ↳ `severityName` | string | Severity name | +| ↳ `severityId` | string | Severity ID | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| ↳ `startedAt` | string | Start date | +| ↳ `mitigatedAt` | string | Mitigation date | +| ↳ `resolvedAt` | string | Resolution date | +| ↳ `closedAt` | string | Closed date | +| `totalCount` | number | Total number of incidents returned | + +### `rootly_create_alert` + +Create a new alert in Rootly for on-call notification and routing. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `summary` | string | Yes | The summary of the alert | +| `description` | string | No | A detailed description of the alert | +| `source` | string | No | The source of the alert \(e.g., api, manual, datadog, pagerduty\) | +| `status` | string | No | Alert status \(open, triggered, acknowledged, resolved\) | +| `serviceIds` | string | No | Comma-separated service IDs to attach | +| `groupIds` | string | No | Comma-separated team/group IDs to attach | +| `environmentIds` | string | No | Comma-separated environment IDs to attach | +| `externalId` | string | No | External ID for the alert | +| `externalUrl` | string | No | External URL for the alert | +| `deduplicationKey` | string | No | Alerts sharing the same deduplication key are treated as a single alert | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `alert` | object | The created alert | +| ↳ `id` | string | Unique alert ID | +| ↳ `summary` | string | Alert summary | +| ↳ `description` | string | Alert description | +| ↳ `source` | string | Alert source | +| ↳ `status` | string | Alert status | +| ↳ `externalId` | string | External ID | +| ↳ `externalUrl` | string | External URL | +| ↳ `deduplicationKey` | string | Deduplication key | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | + +### `rootly_list_alerts` + +List alerts from Rootly with optional filtering by status, source, and services. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `status` | string | No | Filter by status \(open, triggered, acknowledged, resolved\) | +| `source` | string | No | Filter by source \(e.g., api, datadog, pagerduty\) | +| `services` | string | No | Filter by service slugs \(comma-separated\) | +| `environments` | string | No | Filter by environment slugs \(comma-separated\) | +| `groups` | string | No | Filter by team/group slugs \(comma-separated\) | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `alerts` | array | List of alerts | +| ↳ `id` | string | Unique alert ID | +| ↳ `summary` | string | Alert summary | +| ↳ `description` | string | Alert description | +| ↳ `source` | string | Alert source | +| ↳ `status` | string | Alert status | +| ↳ `externalId` | string | External ID | +| ↳ `externalUrl` | string | External URL | +| ↳ `deduplicationKey` | string | Deduplication key | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of alerts returned | + +### `rootly_add_incident_event` + +Add a timeline event to an existing incident in Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `incidentId` | string | Yes | The ID of the incident to add the event to | +| `event` | string | Yes | The summary/description of the event | +| `visibility` | string | No | Event visibility \(internal or external\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `eventId` | string | The ID of the created event | +| `event` | string | The event summary | +| `visibility` | string | Event visibility \(internal or external\) | +| `occurredAt` | string | When the event occurred | +| `createdAt` | string | Creation date | + +### `rootly_list_services` + +List services from Rootly with optional search filtering. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `search` | string | No | Search term to filter services | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `services` | array | List of services | +| ↳ `id` | string | Unique service ID | +| ↳ `name` | string | Service name | +| ↳ `slug` | string | Service slug | +| ↳ `description` | string | Service description | +| ↳ `color` | string | Service color | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of services returned | + +### `rootly_list_severities` + +List severity levels configured in Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `severities` | array | List of severity levels | +| ↳ `id` | string | Unique severity ID | +| ↳ `name` | string | Severity name | +| ↳ `slug` | string | Severity slug | +| ↳ `description` | string | Severity description | +| ↳ `severity` | string | Severity level \(critical, high, medium, low\) | +| ↳ `color` | string | Severity color | +| ↳ `position` | number | Display position | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of severities returned | + +### `rootly_list_teams` + +List teams (groups) configured in Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `search` | string | No | Search term to filter teams | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `teams` | array | List of teams | +| ↳ `id` | string | Unique team ID | +| ↳ `name` | string | Team name | +| ↳ `slug` | string | Team slug | +| ↳ `description` | string | Team description | +| ↳ `color` | string | Team color | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of teams returned | + +### `rootly_list_environments` + +List environments configured in Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `search` | string | No | Search term to filter environments | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `environments` | array | List of environments | +| ↳ `id` | string | Unique environment ID | +| ↳ `name` | string | Environment name | +| ↳ `slug` | string | Environment slug | +| ↳ `description` | string | Environment description | +| ↳ `color` | string | Environment color | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of environments returned | + +### `rootly_list_incident_types` + +List incident types configured in Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `search` | string | No | Search term to filter incident types | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `incidentTypes` | array | List of incident types | +| ↳ `id` | string | Unique incident type ID | +| ↳ `name` | string | Incident type name | +| ↳ `slug` | string | Incident type slug | +| ↳ `description` | string | Incident type description | +| ↳ `color` | string | Incident type color | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of incident types returned | + +### `rootly_list_functionalities` + +List functionalities configured in Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `search` | string | No | Search term to filter functionalities | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `functionalities` | array | List of functionalities | +| ↳ `id` | string | Unique functionality ID | +| ↳ `name` | string | Functionality name | +| ↳ `slug` | string | Functionality slug | +| ↳ `description` | string | Functionality description | +| ↳ `color` | string | Functionality color | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of functionalities returned | + +### `rootly_list_retrospectives` + +List incident retrospectives (post-mortems) from Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `status` | string | No | Filter by status \(draft, published\) | +| `search` | string | No | Search term to filter retrospectives | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `retrospectives` | array | List of retrospectives | +| ↳ `id` | string | Unique retrospective ID | +| ↳ `title` | string | Retrospective title | +| ↳ `status` | string | Status \(draft or published\) | +| ↳ `url` | string | URL to the retrospective | +| ↳ `startedAt` | string | Incident start date | +| ↳ `mitigatedAt` | string | Mitigation date | +| ↳ `resolvedAt` | string | Resolution date | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of retrospectives returned | + + diff --git a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts index 7b9b36c977a..26162d9a582 100644 --- a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts +++ b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts @@ -139,6 +139,7 @@ import { ResendIcon, RevenueCatIcon, RipplingIcon, + RootlyIcon, S3Icon, SalesforceIcon, SearchIcon, @@ -320,6 +321,7 @@ export const blockTypeToIconMap: Record = { resend: ResendIcon, revenuecat: RevenueCatIcon, rippling: RipplingIcon, + rootly: RootlyIcon, s3: S3Icon, salesforce: SalesforceIcon, search: SearchIcon, diff --git a/apps/sim/app/(landing)/integrations/data/integrations.json b/apps/sim/app/(landing)/integrations/data/integrations.json index ae89ed9506e..3b578762f43 100644 --- a/apps/sim/app/(landing)/integrations/data/integrations.json +++ b/apps/sim/app/(landing)/integrations/data/integrations.json @@ -9625,11 +9625,11 @@ }, { "name": "Update Object Category", - "description": "Update a object category" + "description": "Update an object category" }, { "name": "Delete Object Category", - "description": "Delete a object category" + "description": "Delete an object category" }, { "name": "Get Report Run", @@ -9652,6 +9652,81 @@ "integrationType": "hr", "tags": ["hiring"] }, + { + "type": "rootly", + "slug": "rootly", + "name": "Rootly", + "description": "Manage incidents, alerts, and on-call with Rootly", + "longDescription": "Integrate Rootly incident management into workflows. Create and manage incidents, alerts, services, severities, and retrospectives.", + "bgColor": "#6C72C8", + "iconName": "RootlyIcon", + "docsUrl": "https://docs.sim.ai/tools/rootly", + "operations": [ + { + "name": "Create Incident", + "description": "Create a new incident in Rootly with optional severity, services, and teams." + }, + { + "name": "Get Incident", + "description": "Retrieve a single incident by ID from Rootly." + }, + { + "name": "Update Incident", + "description": "Update an existing incident in Rootly (status, severity, summary, etc.)." + }, + { + "name": "List Incidents", + "description": "List incidents from Rootly with optional filtering by status, severity, and more." + }, + { + "name": "Create Alert", + "description": "Create a new alert in Rootly for on-call notification and routing." + }, + { + "name": "List Alerts", + "description": "List alerts from Rootly with optional filtering by status, source, and services." + }, + { + "name": "Add Incident Event", + "description": "Add a timeline event to an existing incident in Rootly." + }, + { + "name": "List Services", + "description": "List services from Rootly with optional search filtering." + }, + { + "name": "List Severities", + "description": "List severity levels configured in Rootly." + }, + { + "name": "List Teams", + "description": "List teams (groups) configured in Rootly." + }, + { + "name": "List Environments", + "description": "List environments configured in Rootly." + }, + { + "name": "List Incident Types", + "description": "List incident types configured in Rootly." + }, + { + "name": "List Functionalities", + "description": "List functionalities configured in Rootly." + }, + { + "name": "List Retrospectives", + "description": "List incident retrospectives (post-mortems) from Rootly." + } + ], + "operationCount": 14, + "triggers": [], + "triggerCount": 0, + "authType": "api-key", + "category": "tools", + "integrationType": "developer-tools", + "tags": ["incident-management", "monitoring"] + }, { "type": "s3", "slug": "s3", diff --git a/apps/sim/blocks/blocks/rootly.ts b/apps/sim/blocks/blocks/rootly.ts new file mode 100644 index 00000000000..36efaa9a878 --- /dev/null +++ b/apps/sim/blocks/blocks/rootly.ts @@ -0,0 +1,829 @@ +import { RootlyIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode, IntegrationType } from '@/blocks/types' +import type { RootlyResponse } from '@/tools/rootly/types' + +export const RootlyBlock: BlockConfig = { + type: 'rootly', + name: 'Rootly', + description: 'Manage incidents, alerts, and on-call with Rootly', + authMode: AuthMode.ApiKey, + longDescription: + 'Integrate Rootly incident management into workflows. Create and manage incidents, alerts, services, severities, and retrospectives.', + docsLink: 'https://docs.sim.ai/tools/rootly', + category: 'tools', + integrationType: IntegrationType.DeveloperTools, + tags: ['incident-management', 'monitoring'], + bgColor: '#6C72C8', + icon: RootlyIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Create Incident', id: 'rootly_create_incident' }, + { label: 'Get Incident', id: 'rootly_get_incident' }, + { label: 'Update Incident', id: 'rootly_update_incident' }, + { label: 'List Incidents', id: 'rootly_list_incidents' }, + { label: 'Create Alert', id: 'rootly_create_alert' }, + { label: 'List Alerts', id: 'rootly_list_alerts' }, + { label: 'Add Incident Event', id: 'rootly_add_incident_event' }, + { label: 'List Services', id: 'rootly_list_services' }, + { label: 'List Severities', id: 'rootly_list_severities' }, + { label: 'List Teams', id: 'rootly_list_teams' }, + { label: 'List Environments', id: 'rootly_list_environments' }, + { label: 'List Incident Types', id: 'rootly_list_incident_types' }, + { label: 'List Functionalities', id: 'rootly_list_functionalities' }, + { label: 'List Retrospectives', id: 'rootly_list_retrospectives' }, + ], + value: () => 'rootly_create_incident', + }, + + // Create Incident fields + { + id: 'title', + title: 'Title', + type: 'short-input', + placeholder: 'Incident title', + condition: { field: 'operation', value: 'rootly_create_incident' }, + required: { field: 'operation', value: 'rootly_create_incident' }, + }, + { + id: 'createSummary', + title: 'Summary', + type: 'long-input', + placeholder: 'Describe the incident', + condition: { field: 'operation', value: 'rootly_create_incident' }, + }, + { + id: 'createSeverityId', + title: 'Severity ID', + type: 'short-input', + placeholder: 'Severity ID (use List Severities to find IDs)', + condition: { field: 'operation', value: 'rootly_create_incident' }, + mode: 'advanced', + }, + { + id: 'createStatus', + title: 'Status', + type: 'dropdown', + options: [ + { label: 'In Triage', id: 'in_triage' }, + { label: 'Started', id: 'started' }, + { label: 'Detected', id: 'detected' }, + { label: 'Acknowledged', id: 'acknowledged' }, + { label: 'Mitigated', id: 'mitigated' }, + { label: 'Resolved', id: 'resolved' }, + { label: 'Closed', id: 'closed' }, + { label: 'Cancelled', id: 'cancelled' }, + ], + condition: { field: 'operation', value: 'rootly_create_incident' }, + mode: 'advanced', + }, + { + id: 'createKind', + title: 'Kind', + type: 'dropdown', + options: [ + { label: 'Normal', id: 'normal' }, + { label: 'Test', id: 'test' }, + { label: 'Example', id: 'example' }, + { label: 'Backfilled', id: 'backfilled' }, + { label: 'Scheduled', id: 'scheduled' }, + ], + condition: { field: 'operation', value: 'rootly_create_incident' }, + mode: 'advanced', + }, + { + id: 'createServiceIds', + title: 'Service IDs', + type: 'short-input', + placeholder: 'Comma-separated service IDs', + condition: { field: 'operation', value: 'rootly_create_incident' }, + mode: 'advanced', + }, + { + id: 'createEnvironmentIds', + title: 'Environment IDs', + type: 'short-input', + placeholder: 'Comma-separated environment IDs', + condition: { field: 'operation', value: 'rootly_create_incident' }, + mode: 'advanced', + }, + { + id: 'createGroupIds', + title: 'Team IDs', + type: 'short-input', + placeholder: 'Comma-separated team/group IDs', + condition: { field: 'operation', value: 'rootly_create_incident' }, + mode: 'advanced', + }, + { + id: 'createIncidentTypeIds', + title: 'Incident Type IDs', + type: 'short-input', + placeholder: 'Comma-separated incident type IDs', + condition: { field: 'operation', value: 'rootly_create_incident' }, + mode: 'advanced', + }, + { + id: 'createFunctionalityIds', + title: 'Functionality IDs', + type: 'short-input', + placeholder: 'Comma-separated functionality IDs', + condition: { field: 'operation', value: 'rootly_create_incident' }, + mode: 'advanced', + }, + { + id: 'createLabels', + title: 'Labels', + type: 'short-input', + placeholder: '{"platform":"osx","version":"1.29"}', + condition: { field: 'operation', value: 'rootly_create_incident' }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: + 'Generate a JSON object of key-value label pairs for a Rootly incident. Example: {"platform":"osx","version":"1.29","region":"us-east-1"}. Return ONLY the JSON object - no explanations, no extra text.', + placeholder: 'Describe the labels (e.g., "platform osx, version 1.29")...', + generationType: 'json-object', + }, + }, + + // Get Incident fields + { + id: 'getIncidentId', + title: 'Incident ID', + type: 'short-input', + placeholder: 'The ID of the incident to retrieve', + condition: { field: 'operation', value: 'rootly_get_incident' }, + required: { field: 'operation', value: 'rootly_get_incident' }, + }, + + // Update Incident fields + { + id: 'updateIncidentId', + title: 'Incident ID', + type: 'short-input', + placeholder: 'The ID of the incident to update', + condition: { field: 'operation', value: 'rootly_update_incident' }, + required: { field: 'operation', value: 'rootly_update_incident' }, + }, + { + id: 'updateTitle', + title: 'Title', + type: 'short-input', + placeholder: 'Updated incident title', + condition: { field: 'operation', value: 'rootly_update_incident' }, + }, + { + id: 'updateSummary', + title: 'Summary', + type: 'long-input', + placeholder: 'Updated incident summary', + condition: { field: 'operation', value: 'rootly_update_incident' }, + }, + { + id: 'updateStatus', + title: 'Status', + type: 'dropdown', + options: [ + { label: 'In Triage', id: 'in_triage' }, + { label: 'Started', id: 'started' }, + { label: 'Detected', id: 'detected' }, + { label: 'Acknowledged', id: 'acknowledged' }, + { label: 'Mitigated', id: 'mitigated' }, + { label: 'Resolved', id: 'resolved' }, + { label: 'Closed', id: 'closed' }, + { label: 'Cancelled', id: 'cancelled' }, + ], + condition: { field: 'operation', value: 'rootly_update_incident' }, + }, + { + id: 'updateSeverityId', + title: 'Severity ID', + type: 'short-input', + placeholder: 'Updated severity ID', + condition: { field: 'operation', value: 'rootly_update_incident' }, + mode: 'advanced', + }, + { + id: 'mitigationMessage', + title: 'Mitigation Message', + type: 'long-input', + placeholder: 'How was the incident mitigated?', + condition: { field: 'operation', value: 'rootly_update_incident' }, + mode: 'advanced', + }, + { + id: 'resolutionMessage', + title: 'Resolution Message', + type: 'long-input', + placeholder: 'How was the incident resolved?', + condition: { field: 'operation', value: 'rootly_update_incident' }, + mode: 'advanced', + }, + { + id: 'updateServiceIds', + title: 'Service IDs', + type: 'short-input', + placeholder: 'Comma-separated service IDs', + condition: { field: 'operation', value: 'rootly_update_incident' }, + mode: 'advanced', + }, + { + id: 'updateGroupIds', + title: 'Team IDs', + type: 'short-input', + placeholder: 'Comma-separated team/group IDs', + condition: { field: 'operation', value: 'rootly_update_incident' }, + mode: 'advanced', + }, + + // List Incidents fields + { + id: 'listIncidentsStatus', + title: 'Status Filter', + type: 'dropdown', + options: [ + { label: 'All', id: '' }, + { label: 'In Triage', id: 'in_triage' }, + { label: 'Started', id: 'started' }, + { label: 'Detected', id: 'detected' }, + { label: 'Acknowledged', id: 'acknowledged' }, + { label: 'Mitigated', id: 'mitigated' }, + { label: 'Resolved', id: 'resolved' }, + { label: 'Closed', id: 'closed' }, + { label: 'Cancelled', id: 'cancelled' }, + ], + condition: { field: 'operation', value: 'rootly_list_incidents' }, + }, + { + id: 'listIncidentsSearch', + title: 'Search', + type: 'short-input', + placeholder: 'Search incidents...', + condition: { field: 'operation', value: 'rootly_list_incidents' }, + }, + { + id: 'listIncidentsSeverity', + title: 'Severity Filter', + type: 'short-input', + placeholder: 'Severity slug (e.g., sev0)', + condition: { field: 'operation', value: 'rootly_list_incidents' }, + mode: 'advanced', + }, + { + id: 'listIncidentsServices', + title: 'Services Filter', + type: 'short-input', + placeholder: 'Comma-separated service slugs', + condition: { field: 'operation', value: 'rootly_list_incidents' }, + mode: 'advanced', + }, + { + id: 'listIncidentsSort', + title: 'Sort', + type: 'dropdown', + options: [ + { label: 'Newest First', id: '-created_at' }, + { label: 'Oldest First', id: 'created_at' }, + { label: 'Recently Started', id: '-started_at' }, + { label: 'Recently Updated', id: '-updated_at' }, + ], + condition: { field: 'operation', value: 'rootly_list_incidents' }, + mode: 'advanced', + }, + { + id: 'listIncidentsPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_incidents' }, + mode: 'advanced', + }, + + // Create Alert fields + { + id: 'alertSummary', + title: 'Summary', + type: 'short-input', + placeholder: 'Alert summary', + condition: { field: 'operation', value: 'rootly_create_alert' }, + required: { field: 'operation', value: 'rootly_create_alert' }, + }, + { + id: 'alertDescription', + title: 'Description', + type: 'long-input', + placeholder: 'Detailed alert description', + condition: { field: 'operation', value: 'rootly_create_alert' }, + }, + { + id: 'alertSource', + title: 'Source', + type: 'short-input', + placeholder: 'Alert source (e.g., api, datadog)', + condition: { field: 'operation', value: 'rootly_create_alert' }, + mode: 'advanced', + }, + { + id: 'alertServiceIds', + title: 'Service IDs', + type: 'short-input', + placeholder: 'Comma-separated service IDs', + condition: { field: 'operation', value: 'rootly_create_alert' }, + mode: 'advanced', + }, + { + id: 'alertGroupIds', + title: 'Team IDs', + type: 'short-input', + placeholder: 'Comma-separated team/group IDs', + condition: { field: 'operation', value: 'rootly_create_alert' }, + mode: 'advanced', + }, + { + id: 'alertDeduplicationKey', + title: 'Deduplication Key', + type: 'short-input', + placeholder: 'Key to deduplicate alerts', + condition: { field: 'operation', value: 'rootly_create_alert' }, + mode: 'advanced', + }, + { + id: 'alertExternalUrl', + title: 'External URL', + type: 'short-input', + placeholder: 'Link to external source', + condition: { field: 'operation', value: 'rootly_create_alert' }, + mode: 'advanced', + }, + + // List Alerts fields + { + id: 'listAlertsStatus', + title: 'Status Filter', + type: 'dropdown', + options: [ + { label: 'All', id: '' }, + { label: 'Open', id: 'open' }, + { label: 'Triggered', id: 'triggered' }, + { label: 'Acknowledged', id: 'acknowledged' }, + { label: 'Resolved', id: 'resolved' }, + ], + condition: { field: 'operation', value: 'rootly_list_alerts' }, + }, + { + id: 'listAlertsSource', + title: 'Source Filter', + type: 'short-input', + placeholder: 'Filter by source (e.g., datadog)', + condition: { field: 'operation', value: 'rootly_list_alerts' }, + mode: 'advanced', + }, + { + id: 'listAlertsPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_alerts' }, + mode: 'advanced', + }, + + // Add Incident Event fields + { + id: 'eventIncidentId', + title: 'Incident ID', + type: 'short-input', + placeholder: 'The ID of the incident', + condition: { field: 'operation', value: 'rootly_add_incident_event' }, + required: { field: 'operation', value: 'rootly_add_incident_event' }, + }, + { + id: 'eventText', + title: 'Event', + type: 'long-input', + placeholder: 'Describe the timeline event', + condition: { field: 'operation', value: 'rootly_add_incident_event' }, + required: { field: 'operation', value: 'rootly_add_incident_event' }, + }, + { + id: 'eventVisibility', + title: 'Visibility', + type: 'dropdown', + options: [ + { label: 'Internal', id: 'internal' }, + { label: 'External', id: 'external' }, + ], + condition: { field: 'operation', value: 'rootly_add_incident_event' }, + mode: 'advanced', + }, + + // List Services fields + { + id: 'servicesSearch', + title: 'Search', + type: 'short-input', + placeholder: 'Search services...', + condition: { field: 'operation', value: 'rootly_list_services' }, + }, + { + id: 'servicesPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_services' }, + mode: 'advanced', + }, + + // List Severities fields + { + id: 'severitiesPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_severities' }, + mode: 'advanced', + }, + + // List Teams fields + { + id: 'teamsSearch', + title: 'Search', + type: 'short-input', + placeholder: 'Search teams...', + condition: { field: 'operation', value: 'rootly_list_teams' }, + }, + { + id: 'teamsPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_teams' }, + mode: 'advanced', + }, + + // List Environments fields + { + id: 'environmentsSearch', + title: 'Search', + type: 'short-input', + placeholder: 'Search environments...', + condition: { field: 'operation', value: 'rootly_list_environments' }, + }, + { + id: 'environmentsPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_environments' }, + mode: 'advanced', + }, + + // List Incident Types fields + { + id: 'incidentTypesSearch', + title: 'Search', + type: 'short-input', + placeholder: 'Search incident types...', + condition: { field: 'operation', value: 'rootly_list_incident_types' }, + }, + { + id: 'incidentTypesPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_incident_types' }, + mode: 'advanced', + }, + + // List Functionalities fields + { + id: 'functionalitiesSearch', + title: 'Search', + type: 'short-input', + placeholder: 'Search functionalities...', + condition: { field: 'operation', value: 'rootly_list_functionalities' }, + }, + { + id: 'functionalitiesPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_functionalities' }, + mode: 'advanced', + }, + + // List Retrospectives fields + { + id: 'retrospectivesStatus', + title: 'Status Filter', + type: 'dropdown', + options: [ + { label: 'All', id: '' }, + { label: 'Draft', id: 'draft' }, + { label: 'Published', id: 'published' }, + ], + condition: { field: 'operation', value: 'rootly_list_retrospectives' }, + }, + { + id: 'retrospectivesSearch', + title: 'Search', + type: 'short-input', + placeholder: 'Search retrospectives...', + condition: { field: 'operation', value: 'rootly_list_retrospectives' }, + }, + { + id: 'retrospectivesPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_retrospectives' }, + mode: 'advanced', + }, + + // API Key (common) + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + placeholder: 'Enter your Rootly API key', + password: true, + required: true, + }, + ], + tools: { + access: [ + 'rootly_create_incident', + 'rootly_get_incident', + 'rootly_update_incident', + 'rootly_list_incidents', + 'rootly_create_alert', + 'rootly_list_alerts', + 'rootly_add_incident_event', + 'rootly_list_services', + 'rootly_list_severities', + 'rootly_list_teams', + 'rootly_list_environments', + 'rootly_list_incident_types', + 'rootly_list_functionalities', + 'rootly_list_retrospectives', + ], + config: { + tool: (params) => params.operation, + params: (params) => { + const baseParams: Record = { + apiKey: params.apiKey, + } + + switch (params.operation) { + case 'rootly_create_incident': + return { + ...baseParams, + title: params.title, + summary: params.createSummary, + severityId: params.createSeverityId, + status: params.createStatus, + kind: params.createKind, + serviceIds: params.createServiceIds, + environmentIds: params.createEnvironmentIds, + groupIds: params.createGroupIds, + incidentTypeIds: params.createIncidentTypeIds, + functionalityIds: params.createFunctionalityIds, + labels: params.createLabels, + } + + case 'rootly_get_incident': + return { + ...baseParams, + incidentId: params.getIncidentId, + } + + case 'rootly_update_incident': + return { + ...baseParams, + incidentId: params.updateIncidentId, + title: params.updateTitle, + summary: params.updateSummary, + status: params.updateStatus, + severityId: params.updateSeverityId, + mitigationMessage: params.mitigationMessage, + resolutionMessage: params.resolutionMessage, + serviceIds: params.updateServiceIds, + groupIds: params.updateGroupIds, + } + + case 'rootly_list_incidents': + return { + ...baseParams, + status: params.listIncidentsStatus, + search: params.listIncidentsSearch, + severity: params.listIncidentsSeverity, + services: params.listIncidentsServices, + sort: params.listIncidentsSort, + pageSize: params.listIncidentsPageSize + ? Number(params.listIncidentsPageSize) + : undefined, + } + + case 'rootly_create_alert': + return { + ...baseParams, + summary: params.alertSummary, + description: params.alertDescription, + source: params.alertSource, + serviceIds: params.alertServiceIds, + groupIds: params.alertGroupIds, + deduplicationKey: params.alertDeduplicationKey, + externalUrl: params.alertExternalUrl, + } + + case 'rootly_list_alerts': + return { + ...baseParams, + status: params.listAlertsStatus, + source: params.listAlertsSource, + pageSize: params.listAlertsPageSize ? Number(params.listAlertsPageSize) : undefined, + } + + case 'rootly_add_incident_event': + return { + ...baseParams, + incidentId: params.eventIncidentId, + event: params.eventText, + visibility: params.eventVisibility, + } + + case 'rootly_list_services': + return { + ...baseParams, + search: params.servicesSearch, + pageSize: params.servicesPageSize ? Number(params.servicesPageSize) : undefined, + } + + case 'rootly_list_severities': + return { + ...baseParams, + pageSize: params.severitiesPageSize ? Number(params.severitiesPageSize) : undefined, + } + + case 'rootly_list_teams': + return { + ...baseParams, + search: params.teamsSearch, + pageSize: params.teamsPageSize ? Number(params.teamsPageSize) : undefined, + } + + case 'rootly_list_environments': + return { + ...baseParams, + search: params.environmentsSearch, + pageSize: params.environmentsPageSize + ? Number(params.environmentsPageSize) + : undefined, + } + + case 'rootly_list_incident_types': + return { + ...baseParams, + search: params.incidentTypesSearch, + pageSize: params.incidentTypesPageSize + ? Number(params.incidentTypesPageSize) + : undefined, + } + + case 'rootly_list_functionalities': + return { + ...baseParams, + search: params.functionalitiesSearch, + pageSize: params.functionalitiesPageSize + ? Number(params.functionalitiesPageSize) + : undefined, + } + + case 'rootly_list_retrospectives': + return { + ...baseParams, + status: params.retrospectivesStatus, + search: params.retrospectivesSearch, + pageSize: params.retrospectivesPageSize + ? Number(params.retrospectivesPageSize) + : undefined, + } + + default: + return baseParams + } + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Rootly API key' }, + title: { type: 'string', description: 'Incident title' }, + createSummary: { type: 'string', description: 'Incident summary' }, + createSeverityId: { type: 'string', description: 'Severity ID' }, + createStatus: { type: 'string', description: 'Incident status' }, + createKind: { type: 'string', description: 'Incident kind' }, + createServiceIds: { type: 'string', description: 'Service IDs' }, + createEnvironmentIds: { type: 'string', description: 'Environment IDs' }, + createGroupIds: { type: 'string', description: 'Team IDs' }, + createIncidentTypeIds: { type: 'string', description: 'Incident type IDs' }, + createFunctionalityIds: { type: 'string', description: 'Functionality IDs' }, + createLabels: { type: 'string', description: 'Labels as JSON' }, + getIncidentId: { type: 'string', description: 'Incident ID to retrieve' }, + updateIncidentId: { type: 'string', description: 'Incident ID to update' }, + updateTitle: { type: 'string', description: 'Updated title' }, + updateSummary: { type: 'string', description: 'Updated summary' }, + updateStatus: { type: 'string', description: 'Updated status' }, + updateSeverityId: { type: 'string', description: 'Updated severity ID' }, + mitigationMessage: { type: 'string', description: 'Mitigation message' }, + resolutionMessage: { type: 'string', description: 'Resolution message' }, + updateServiceIds: { type: 'string', description: 'Updated service IDs' }, + updateGroupIds: { type: 'string', description: 'Updated team IDs' }, + listIncidentsStatus: { type: 'string', description: 'Filter by status' }, + listIncidentsSearch: { type: 'string', description: 'Search incidents' }, + listIncidentsSeverity: { type: 'string', description: 'Filter by severity' }, + listIncidentsServices: { type: 'string', description: 'Filter by services' }, + listIncidentsSort: { type: 'string', description: 'Sort order' }, + listIncidentsPageSize: { type: 'string', description: 'Page size' }, + alertSummary: { type: 'string', description: 'Alert summary' }, + alertDescription: { type: 'string', description: 'Alert description' }, + alertSource: { type: 'string', description: 'Alert source' }, + alertServiceIds: { type: 'string', description: 'Alert service IDs' }, + alertGroupIds: { type: 'string', description: 'Alert team IDs' }, + alertDeduplicationKey: { type: 'string', description: 'Deduplication key' }, + alertExternalUrl: { type: 'string', description: 'External URL' }, + listAlertsStatus: { type: 'string', description: 'Filter alerts by status' }, + listAlertsSource: { type: 'string', description: 'Filter alerts by source' }, + listAlertsPageSize: { type: 'string', description: 'Alerts page size' }, + eventIncidentId: { type: 'string', description: 'Incident ID for event' }, + eventText: { type: 'string', description: 'Event description' }, + eventVisibility: { type: 'string', description: 'Event visibility' }, + servicesSearch: { type: 'string', description: 'Search services' }, + servicesPageSize: { type: 'string', description: 'Services page size' }, + severitiesPageSize: { type: 'string', description: 'Severities page size' }, + teamsSearch: { type: 'string', description: 'Search teams' }, + teamsPageSize: { type: 'string', description: 'Teams page size' }, + environmentsSearch: { type: 'string', description: 'Search environments' }, + environmentsPageSize: { type: 'string', description: 'Environments page size' }, + incidentTypesSearch: { type: 'string', description: 'Search incident types' }, + incidentTypesPageSize: { type: 'string', description: 'Incident types page size' }, + functionalitiesSearch: { type: 'string', description: 'Search functionalities' }, + functionalitiesPageSize: { type: 'string', description: 'Functionalities page size' }, + retrospectivesStatus: { type: 'string', description: 'Filter retrospectives by status' }, + retrospectivesSearch: { type: 'string', description: 'Search retrospectives' }, + retrospectivesPageSize: { type: 'string', description: 'Retrospectives page size' }, + }, + outputs: { + incident: { + type: 'json', + description: 'Incident data (id, title, status, summary, severity, url, timestamps)', + }, + incidents: { + type: 'json', + description: 'List of incidents (id, title, status, summary, severity, url, timestamps)', + }, + alert: { + type: 'json', + description: 'Alert data (id, summary, description, status, source, externalUrl)', + }, + alerts: { + type: 'json', + description: 'List of alerts (id, summary, description, status, source, externalUrl)', + }, + eventId: { type: 'string', description: 'Created event ID' }, + event: { type: 'string', description: 'Event description' }, + visibility: { type: 'string', description: 'Event visibility' }, + occurredAt: { type: 'string', description: 'When the event occurred' }, + createdAt: { type: 'string', description: 'Creation date' }, + services: { + type: 'json', + description: 'List of services (id, name, slug, description, color)', + }, + severities: { + type: 'json', + description: 'List of severities (id, name, slug, severity, color, position)', + }, + teams: { type: 'json', description: 'List of teams (id, name, slug, description, color)' }, + environments: { + type: 'json', + description: 'List of environments (id, name, slug, description, color)', + }, + incidentTypes: { + type: 'json', + description: 'List of incident types (id, name, slug, description, color)', + }, + functionalities: { + type: 'json', + description: 'List of functionalities (id, name, slug, description, color)', + }, + retrospectives: { + type: 'json', + description: 'List of retrospectives (id, title, status, url, timestamps)', + }, + totalCount: { type: 'number', description: 'Total count of items returned' }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index 574fdd000eb..6f24fe7ba61 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -151,6 +151,7 @@ import { ResendBlock } from '@/blocks/blocks/resend' import { ResponseBlock } from '@/blocks/blocks/response' import { RevenueCatBlock } from '@/blocks/blocks/revenuecat' import { RipplingBlock } from '@/blocks/blocks/rippling' +import { RootlyBlock } from '@/blocks/blocks/rootly' import { RouterBlock, RouterV2Block } from '@/blocks/blocks/router' import { RssBlock } from '@/blocks/blocks/rss' import { S3Block } from '@/blocks/blocks/s3' @@ -382,6 +383,7 @@ export const registry: Record = { response: ResponseBlock, revenuecat: RevenueCatBlock, rippling: RipplingBlock, + rootly: RootlyBlock, router: RouterBlock, router_v2: RouterV2Block, rss: RssBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 6614c9f6036..35cf31a2ce4 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -6387,6 +6387,42 @@ export function RipplingIcon(props: SVGProps) { ) } +export function RootlyIcon(props: SVGProps) { + return ( + + + + + + + + + + + ) +} + export function HexIcon(props: SVGProps) { return ( diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 09da0ce4bf5..9e6c2467ade 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -1985,6 +1985,22 @@ import { ripplingUpdateTitleTool, ripplingUpdateWorkLocationTool, } from '@/tools/rippling' +import { + rootlyAddIncidentEventTool, + rootlyCreateAlertTool, + rootlyCreateIncidentTool, + rootlyGetIncidentTool, + rootlyListAlertsTool, + rootlyListEnvironmentsTool, + rootlyListFunctionalitiesTool, + rootlyListIncidentsTool, + rootlyListIncidentTypesTool, + rootlyListRetrospectivesTool, + rootlyListServicesTool, + rootlyListSeveritiesTool, + rootlyListTeamsTool, + rootlyUpdateIncidentTool, +} from '@/tools/rootly' import { s3CopyObjectTool, s3DeleteObjectTool, @@ -3774,6 +3790,20 @@ export const tools: Record = { rippling_update_supergroup_inclusion_members: ripplingUpdateSupergroupInclusionMembersTool, rippling_update_title: ripplingUpdateTitleTool, rippling_update_work_location: ripplingUpdateWorkLocationTool, + rootly_add_incident_event: rootlyAddIncidentEventTool, + rootly_create_alert: rootlyCreateAlertTool, + rootly_create_incident: rootlyCreateIncidentTool, + rootly_get_incident: rootlyGetIncidentTool, + rootly_list_alerts: rootlyListAlertsTool, + rootly_list_environments: rootlyListEnvironmentsTool, + rootly_list_functionalities: rootlyListFunctionalitiesTool, + rootly_list_incidents: rootlyListIncidentsTool, + rootly_list_incident_types: rootlyListIncidentTypesTool, + rootly_list_retrospectives: rootlyListRetrospectivesTool, + rootly_list_services: rootlyListServicesTool, + rootly_list_severities: rootlyListSeveritiesTool, + rootly_list_teams: rootlyListTeamsTool, + rootly_update_incident: rootlyUpdateIncidentTool, google_drive_copy: googleDriveCopyTool, google_drive_create_folder: googleDriveCreateFolderTool, google_drive_delete: googleDriveDeleteTool, diff --git a/apps/sim/tools/rootly/add_incident_event.ts b/apps/sim/tools/rootly/add_incident_event.ts new file mode 100644 index 00000000000..c1343ad1140 --- /dev/null +++ b/apps/sim/tools/rootly/add_incident_event.ts @@ -0,0 +1,111 @@ +import type { + RootlyAddIncidentEventParams, + RootlyAddIncidentEventResponse, +} from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyAddIncidentEventTool: ToolConfig< + RootlyAddIncidentEventParams, + RootlyAddIncidentEventResponse +> = { + id: 'rootly_add_incident_event', + name: 'Rootly Add Incident Event', + description: 'Add a timeline event to an existing incident in Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + incidentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the incident to add the event to', + }, + event: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The summary/description of the event', + }, + visibility: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Event visibility (internal or external)', + }, + }, + + request: { + url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/events`, + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + body: (params) => { + const attributes: Record = { + event: params.event, + } + if (params.visibility) attributes.visibility = params.visibility + return { data: { type: 'incident_events', attributes } } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { + eventId: '', + event: '', + visibility: null, + occurredAt: null, + createdAt: '', + }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const attrs = data.data?.attributes || {} + return { + success: true, + output: { + eventId: data.data?.id ?? '', + event: attrs.event ?? '', + visibility: attrs.visibility ?? null, + occurredAt: attrs.occurred_at ?? null, + createdAt: attrs.created_at ?? '', + }, + } + }, + + outputs: { + eventId: { + type: 'string', + description: 'The ID of the created event', + }, + event: { + type: 'string', + description: 'The event summary', + }, + visibility: { + type: 'string', + description: 'Event visibility (internal or external)', + }, + occurredAt: { + type: 'string', + description: 'When the event occurred', + }, + createdAt: { + type: 'string', + description: 'Creation date', + }, + }, +} diff --git a/apps/sim/tools/rootly/create_alert.ts b/apps/sim/tools/rootly/create_alert.ts new file mode 100644 index 00000000000..d289f356cd4 --- /dev/null +++ b/apps/sim/tools/rootly/create_alert.ts @@ -0,0 +1,159 @@ +import type { RootlyCreateAlertParams, RootlyCreateAlertResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyCreateAlertTool: ToolConfig = + { + id: 'rootly_create_alert', + name: 'Rootly Create Alert', + description: 'Create a new alert in Rootly for on-call notification and routing.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + summary: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The summary of the alert', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'A detailed description of the alert', + }, + source: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The source of the alert (e.g., api, manual, datadog, pagerduty)', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Alert status (open, triggered, acknowledged, resolved)', + }, + serviceIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated service IDs to attach', + }, + groupIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated team/group IDs to attach', + }, + environmentIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated environment IDs to attach', + }, + externalId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'External ID for the alert', + }, + externalUrl: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'External URL for the alert', + }, + deduplicationKey: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Alerts sharing the same deduplication key are treated as a single alert', + }, + }, + + request: { + url: 'https://api.rootly.com/v1/alerts', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + body: (params) => { + const attributes: Record = { + summary: params.summary, + } + if (params.description) attributes.description = params.description + if (params.source) attributes.source = params.source + if (params.status) attributes.status = params.status + if (params.externalId) attributes.external_id = params.externalId + if (params.externalUrl) attributes.external_url = params.externalUrl + if (params.deduplicationKey) attributes.deduplication_key = params.deduplicationKey + if (params.serviceIds) { + attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim()) + } + if (params.groupIds) { + attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim()) + } + if (params.environmentIds) { + attributes.environment_ids = params.environmentIds.split(',').map((s: string) => s.trim()) + } + return { data: { type: 'alerts', attributes } } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { alert: {} as RootlyCreateAlertResponse['output']['alert'] }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const attrs = data.data?.attributes || {} + return { + success: true, + output: { + alert: { + id: data.data?.id ?? null, + summary: attrs.summary ?? '', + description: attrs.description ?? null, + source: attrs.source ?? null, + status: attrs.status ?? null, + externalId: attrs.external_id ?? null, + externalUrl: attrs.external_url ?? null, + deduplicationKey: attrs.deduplication_key ?? null, + createdAt: attrs.created_at ?? '', + updatedAt: attrs.updated_at ?? '', + }, + }, + } + }, + + outputs: { + alert: { + type: 'object', + description: 'The created alert', + properties: { + id: { type: 'string', description: 'Unique alert ID' }, + summary: { type: 'string', description: 'Alert summary' }, + description: { type: 'string', description: 'Alert description' }, + source: { type: 'string', description: 'Alert source' }, + status: { type: 'string', description: 'Alert status' }, + externalId: { type: 'string', description: 'External ID' }, + externalUrl: { type: 'string', description: 'External URL' }, + deduplicationKey: { type: 'string', description: 'Deduplication key' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + } diff --git a/apps/sim/tools/rootly/create_incident.ts b/apps/sim/tools/rootly/create_incident.ts new file mode 100644 index 00000000000..ae9d8ccc8fc --- /dev/null +++ b/apps/sim/tools/rootly/create_incident.ts @@ -0,0 +1,208 @@ +import type { RootlyCreateIncidentParams, RootlyCreateIncidentResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyCreateIncidentTool: ToolConfig< + RootlyCreateIncidentParams, + RootlyCreateIncidentResponse +> = { + id: 'rootly_create_incident', + name: 'Rootly Create Incident', + description: 'Create a new incident in Rootly with optional severity, services, and teams.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + title: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The title of the incident', + }, + summary: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'A summary of the incident', + }, + severityId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Severity ID to attach to the incident', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Incident status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled)', + }, + kind: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Incident kind (normal, test, example, backfilled, scheduled)', + }, + serviceIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated service IDs to attach', + }, + environmentIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated environment IDs to attach', + }, + groupIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated team/group IDs to attach', + }, + incidentTypeIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated incident type IDs to attach', + }, + functionalityIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated functionality IDs to attach', + }, + labels: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Labels as JSON object, e.g. {"platform":"osx","version":"1.29"}', + }, + private: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Create as a private incident (cannot be undone)', + }, + }, + + request: { + url: 'https://api.rootly.com/v1/incidents', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + body: (params) => { + const attributes: Record = { + title: params.title, + } + if (params.summary) attributes.summary = params.summary + if (params.severityId) attributes.severity_id = params.severityId + if (params.status) attributes.status = params.status + if (params.kind) attributes.kind = params.kind + if (params.private !== undefined) attributes.private = params.private + if (params.serviceIds) { + attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim()) + } + if (params.environmentIds) { + attributes.environment_ids = params.environmentIds.split(',').map((s: string) => s.trim()) + } + if (params.groupIds) { + attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim()) + } + if (params.incidentTypeIds) { + attributes.incident_type_ids = params.incidentTypeIds + .split(',') + .map((s: string) => s.trim()) + } + if (params.functionalityIds) { + attributes.functionality_ids = params.functionalityIds + .split(',') + .map((s: string) => s.trim()) + } + if (params.labels) { + try { + attributes.labels = JSON.parse(params.labels) + } catch { + attributes.labels = params.labels + } + } + return { data: { type: 'incidents', attributes } } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + const errorMsg = + errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}` + return { + success: false, + output: { incident: {} as RootlyCreateIncidentResponse['output']['incident'] }, + error: errorMsg, + } + } + + const data = await response.json() + const attrs = data.data?.attributes || {} + return { + success: true, + output: { + incident: { + id: data.data?.id ?? null, + sequentialId: attrs.sequential_id ?? null, + title: attrs.title ?? '', + slug: attrs.slug ?? null, + kind: attrs.kind ?? null, + summary: attrs.summary ?? null, + status: attrs.status ?? null, + private: attrs.private ?? false, + url: attrs.url ?? null, + shortUrl: attrs.short_url ?? null, + severityName: attrs.severity?.data?.attributes?.name ?? null, + severityId: attrs.severity?.data?.id ?? null, + createdAt: attrs.created_at ?? '', + updatedAt: attrs.updated_at ?? '', + startedAt: attrs.started_at ?? null, + mitigatedAt: attrs.mitigated_at ?? null, + resolvedAt: attrs.resolved_at ?? null, + closedAt: attrs.closed_at ?? null, + }, + }, + } + }, + + outputs: { + incident: { + type: 'object', + description: 'The created incident', + properties: { + id: { type: 'string', description: 'Unique incident ID' }, + sequentialId: { type: 'number', description: 'Sequential incident number' }, + title: { type: 'string', description: 'Incident title' }, + slug: { type: 'string', description: 'Incident slug' }, + kind: { type: 'string', description: 'Incident kind' }, + summary: { type: 'string', description: 'Incident summary' }, + status: { type: 'string', description: 'Incident status' }, + private: { type: 'boolean', description: 'Whether the incident is private' }, + url: { type: 'string', description: 'URL to the incident' }, + shortUrl: { type: 'string', description: 'Short URL to the incident' }, + severityName: { type: 'string', description: 'Severity name' }, + severityId: { type: 'string', description: 'Severity ID' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + startedAt: { type: 'string', description: 'Start date' }, + mitigatedAt: { type: 'string', description: 'Mitigation date' }, + resolvedAt: { type: 'string', description: 'Resolution date' }, + closedAt: { type: 'string', description: 'Closed date' }, + }, + }, + }, +} diff --git a/apps/sim/tools/rootly/get_incident.ts b/apps/sim/tools/rootly/get_incident.ts new file mode 100644 index 00000000000..cb3b8eaba15 --- /dev/null +++ b/apps/sim/tools/rootly/get_incident.ts @@ -0,0 +1,97 @@ +import type { RootlyGetIncidentParams, RootlyGetIncidentResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyGetIncidentTool: ToolConfig = + { + id: 'rootly_get_incident', + name: 'Rootly Get Incident', + description: 'Retrieve a single incident by ID from Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + incidentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the incident to retrieve', + }, + }, + + request: { + url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}`, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { incident: {} as RootlyGetIncidentResponse['output']['incident'] }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const attrs = data.data?.attributes || {} + return { + success: true, + output: { + incident: { + id: data.data?.id ?? null, + sequentialId: attrs.sequential_id ?? null, + title: attrs.title ?? '', + slug: attrs.slug ?? null, + kind: attrs.kind ?? null, + summary: attrs.summary ?? null, + status: attrs.status ?? null, + private: attrs.private ?? false, + url: attrs.url ?? null, + shortUrl: attrs.short_url ?? null, + severityName: attrs.severity?.data?.attributes?.name ?? null, + severityId: attrs.severity?.data?.id ?? null, + createdAt: attrs.created_at ?? '', + updatedAt: attrs.updated_at ?? '', + startedAt: attrs.started_at ?? null, + mitigatedAt: attrs.mitigated_at ?? null, + resolvedAt: attrs.resolved_at ?? null, + closedAt: attrs.closed_at ?? null, + }, + }, + } + }, + + outputs: { + incident: { + type: 'object', + description: 'The incident details', + properties: { + id: { type: 'string', description: 'Unique incident ID' }, + sequentialId: { type: 'number', description: 'Sequential incident number' }, + title: { type: 'string', description: 'Incident title' }, + slug: { type: 'string', description: 'Incident slug' }, + kind: { type: 'string', description: 'Incident kind' }, + summary: { type: 'string', description: 'Incident summary' }, + status: { type: 'string', description: 'Incident status' }, + url: { type: 'string', description: 'URL to the incident' }, + severityName: { type: 'string', description: 'Severity name' }, + severityId: { type: 'string', description: 'Severity ID' }, + startedAt: { type: 'string', description: 'Start date' }, + mitigatedAt: { type: 'string', description: 'Mitigation date' }, + resolvedAt: { type: 'string', description: 'Resolution date' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + } diff --git a/apps/sim/tools/rootly/index.ts b/apps/sim/tools/rootly/index.ts new file mode 100644 index 00000000000..b6a05b5d4cf --- /dev/null +++ b/apps/sim/tools/rootly/index.ts @@ -0,0 +1,14 @@ +export { rootlyAddIncidentEventTool } from '@/tools/rootly/add_incident_event' +export { rootlyCreateAlertTool } from '@/tools/rootly/create_alert' +export { rootlyCreateIncidentTool } from '@/tools/rootly/create_incident' +export { rootlyGetIncidentTool } from '@/tools/rootly/get_incident' +export { rootlyListAlertsTool } from '@/tools/rootly/list_alerts' +export { rootlyListEnvironmentsTool } from '@/tools/rootly/list_environments' +export { rootlyListFunctionalitiesTool } from '@/tools/rootly/list_functionalities' +export { rootlyListIncidentTypesTool } from '@/tools/rootly/list_incident_types' +export { rootlyListIncidentsTool } from '@/tools/rootly/list_incidents' +export { rootlyListRetrospectivesTool } from '@/tools/rootly/list_retrospectives' +export { rootlyListServicesTool } from '@/tools/rootly/list_services' +export { rootlyListSeveritiesTool } from '@/tools/rootly/list_severities' +export { rootlyListTeamsTool } from '@/tools/rootly/list_teams' +export { rootlyUpdateIncidentTool } from '@/tools/rootly/update_incident' diff --git a/apps/sim/tools/rootly/list_alerts.ts b/apps/sim/tools/rootly/list_alerts.ts new file mode 100644 index 00000000000..cc40bfbeb33 --- /dev/null +++ b/apps/sim/tools/rootly/list_alerts.ts @@ -0,0 +1,142 @@ +import type { RootlyListAlertsParams, RootlyListAlertsResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListAlertsTool: ToolConfig = { + id: 'rootly_list_alerts', + name: 'Rootly List Alerts', + description: 'List alerts from Rootly with optional filtering by status, source, and services.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by status (open, triggered, acknowledged, resolved)', + }, + source: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by source (e.g., api, datadog, pagerduty)', + }, + services: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by service slugs (comma-separated)', + }, + environments: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by environment slugs (comma-separated)', + }, + groups: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by team/group slugs (comma-separated)', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.status) queryParams.set('filter[status]', params.status) + if (params.source) queryParams.set('filter[source]', params.source) + if (params.services) queryParams.set('filter[services]', params.services) + if (params.environments) queryParams.set('filter[environments]', params.environments) + if (params.groups) queryParams.set('filter[groups]', params.groups) + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/alerts${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { alerts: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const alerts = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + summary: (attrs.summary as string) ?? '', + description: (attrs.description as string) ?? null, + source: (attrs.source as string) ?? null, + status: (attrs.status as string) ?? null, + externalId: (attrs.external_id as string) ?? null, + externalUrl: (attrs.external_url as string) ?? null, + deduplicationKey: (attrs.deduplication_key as string) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + alerts, + totalCount: alerts.length, + }, + } + }, + + outputs: { + alerts: { + type: 'array', + description: 'List of alerts', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique alert ID' }, + summary: { type: 'string', description: 'Alert summary' }, + description: { type: 'string', description: 'Alert description' }, + source: { type: 'string', description: 'Alert source' }, + status: { type: 'string', description: 'Alert status' }, + externalId: { type: 'string', description: 'External ID' }, + externalUrl: { type: 'string', description: 'External URL' }, + deduplicationKey: { type: 'string', description: 'Deduplication key' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of alerts returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_environments.ts b/apps/sim/tools/rootly/list_environments.ts new file mode 100644 index 00000000000..cfe01884eaf --- /dev/null +++ b/apps/sim/tools/rootly/list_environments.ts @@ -0,0 +1,114 @@ +import type { + RootlyListEnvironmentsParams, + RootlyListEnvironmentsResponse, +} from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListEnvironmentsTool: ToolConfig< + RootlyListEnvironmentsParams, + RootlyListEnvironmentsResponse +> = { + id: 'rootly_list_environments', + name: 'Rootly List Environments', + description: 'List environments configured in Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + search: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter environments', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.search) queryParams.set('filter[search]', params.search) + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/environments${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { environments: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const environments = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + name: (attrs.name as string) ?? '', + slug: (attrs.slug as string) ?? null, + description: (attrs.description as string) ?? null, + color: (attrs.color as string) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + environments, + totalCount: environments.length, + }, + } + }, + + outputs: { + environments: { + type: 'array', + description: 'List of environments', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique environment ID' }, + name: { type: 'string', description: 'Environment name' }, + slug: { type: 'string', description: 'Environment slug' }, + description: { type: 'string', description: 'Environment description' }, + color: { type: 'string', description: 'Environment color' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of environments returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_functionalities.ts b/apps/sim/tools/rootly/list_functionalities.ts new file mode 100644 index 00000000000..561053b5d07 --- /dev/null +++ b/apps/sim/tools/rootly/list_functionalities.ts @@ -0,0 +1,114 @@ +import type { + RootlyListFunctionalitiesParams, + RootlyListFunctionalitiesResponse, +} from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListFunctionalitiesTool: ToolConfig< + RootlyListFunctionalitiesParams, + RootlyListFunctionalitiesResponse +> = { + id: 'rootly_list_functionalities', + name: 'Rootly List Functionalities', + description: 'List functionalities configured in Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + search: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter functionalities', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.search) queryParams.set('filter[search]', params.search) + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/functionalities${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { functionalities: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const functionalities = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + name: (attrs.name as string) ?? '', + slug: (attrs.slug as string) ?? null, + description: (attrs.description as string) ?? null, + color: (attrs.color as string) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + functionalities, + totalCount: functionalities.length, + }, + } + }, + + outputs: { + functionalities: { + type: 'array', + description: 'List of functionalities', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique functionality ID' }, + name: { type: 'string', description: 'Functionality name' }, + slug: { type: 'string', description: 'Functionality slug' }, + description: { type: 'string', description: 'Functionality description' }, + color: { type: 'string', description: 'Functionality color' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of functionalities returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_incident_types.ts b/apps/sim/tools/rootly/list_incident_types.ts new file mode 100644 index 00000000000..55b860d3cbb --- /dev/null +++ b/apps/sim/tools/rootly/list_incident_types.ts @@ -0,0 +1,114 @@ +import type { + RootlyListIncidentTypesParams, + RootlyListIncidentTypesResponse, +} from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListIncidentTypesTool: ToolConfig< + RootlyListIncidentTypesParams, + RootlyListIncidentTypesResponse +> = { + id: 'rootly_list_incident_types', + name: 'Rootly List Incident Types', + description: 'List incident types configured in Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + search: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter incident types', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.search) queryParams.set('filter[search]', params.search) + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/incident_types${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { incidentTypes: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const incidentTypes = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + name: (attrs.name as string) ?? '', + slug: (attrs.slug as string) ?? null, + description: (attrs.description as string) ?? null, + color: (attrs.color as string) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + incidentTypes, + totalCount: incidentTypes.length, + }, + } + }, + + outputs: { + incidentTypes: { + type: 'array', + description: 'List of incident types', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique incident type ID' }, + name: { type: 'string', description: 'Incident type name' }, + slug: { type: 'string', description: 'Incident type slug' }, + description: { type: 'string', description: 'Incident type description' }, + color: { type: 'string', description: 'Incident type color' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of incident types returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_incidents.ts b/apps/sim/tools/rootly/list_incidents.ts new file mode 100644 index 00000000000..805d1bd6b03 --- /dev/null +++ b/apps/sim/tools/rootly/list_incidents.ts @@ -0,0 +1,179 @@ +import type { RootlyListIncidentsParams, RootlyListIncidentsResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListIncidentsTool: ToolConfig< + RootlyListIncidentsParams, + RootlyListIncidentsResponse +> = { + id: 'rootly_list_incidents', + name: 'Rootly List Incidents', + description: 'List incidents from Rootly with optional filtering by status, severity, and more.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Filter by status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled)', + }, + severity: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by severity slug', + }, + search: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter incidents', + }, + services: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by service slugs (comma-separated)', + }, + teams: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by team slugs (comma-separated)', + }, + environments: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by environment slugs (comma-separated)', + }, + sort: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort order (e.g., -created_at, created_at, -started_at)', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.status) queryParams.set('filter[status]', params.status) + if (params.severity) queryParams.set('filter[severity]', params.severity) + if (params.search) queryParams.set('filter[search]', params.search) + if (params.services) queryParams.set('filter[services]', params.services) + if (params.teams) queryParams.set('filter[teams]', params.teams) + if (params.environments) queryParams.set('filter[environments]', params.environments) + if (params.sort) queryParams.set('sort', params.sort) + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/incidents${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { incidents: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const incidents = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + const severity = attrs.severity as Record | undefined + const severityData = severity?.data as Record | undefined + const severityAttrs = severityData?.attributes as Record | undefined + return { + id: item.id ?? null, + sequentialId: (attrs.sequential_id as number) ?? null, + title: (attrs.title as string) ?? '', + slug: (attrs.slug as string) ?? null, + kind: (attrs.kind as string) ?? null, + summary: (attrs.summary as string) ?? null, + status: (attrs.status as string) ?? null, + private: (attrs.private as boolean) ?? false, + url: (attrs.url as string) ?? null, + shortUrl: (attrs.short_url as string) ?? null, + severityName: (severityAttrs?.name as string) ?? null, + severityId: (severityData?.id as string) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + startedAt: (attrs.started_at as string) ?? null, + mitigatedAt: (attrs.mitigated_at as string) ?? null, + resolvedAt: (attrs.resolved_at as string) ?? null, + closedAt: (attrs.closed_at as string) ?? null, + } + }) + + return { + success: true, + output: { + incidents, + totalCount: incidents.length, + }, + } + }, + + outputs: { + incidents: { + type: 'array', + description: 'List of incidents', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique incident ID' }, + sequentialId: { type: 'number', description: 'Sequential incident number' }, + title: { type: 'string', description: 'Incident title' }, + slug: { type: 'string', description: 'Incident slug' }, + kind: { type: 'string', description: 'Incident kind' }, + summary: { type: 'string', description: 'Incident summary' }, + status: { type: 'string', description: 'Incident status' }, + private: { type: 'boolean', description: 'Whether the incident is private' }, + url: { type: 'string', description: 'URL to the incident' }, + shortUrl: { type: 'string', description: 'Short URL to the incident' }, + severityName: { type: 'string', description: 'Severity name' }, + severityId: { type: 'string', description: 'Severity ID' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + startedAt: { type: 'string', description: 'Start date' }, + mitigatedAt: { type: 'string', description: 'Mitigation date' }, + resolvedAt: { type: 'string', description: 'Resolution date' }, + closedAt: { type: 'string', description: 'Closed date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of incidents returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_retrospectives.ts b/apps/sim/tools/rootly/list_retrospectives.ts new file mode 100644 index 00000000000..81d8fc6d302 --- /dev/null +++ b/apps/sim/tools/rootly/list_retrospectives.ts @@ -0,0 +1,125 @@ +import type { + RootlyListRetrospectivesParams, + RootlyListRetrospectivesResponse, +} from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListRetrospectivesTool: ToolConfig< + RootlyListRetrospectivesParams, + RootlyListRetrospectivesResponse +> = { + id: 'rootly_list_retrospectives', + name: 'Rootly List Retrospectives', + description: 'List incident retrospectives (post-mortems) from Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by status (draft, published)', + }, + search: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter retrospectives', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.status) queryParams.set('filter[status]', params.status) + if (params.search) queryParams.set('filter[search]', params.search) + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/post_mortems${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { retrospectives: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const retrospectives = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + title: (attrs.title as string) ?? '', + status: (attrs.status as string) ?? null, + url: (attrs.url as string) ?? null, + startedAt: (attrs.started_at as string) ?? null, + mitigatedAt: (attrs.mitigated_at as string) ?? null, + resolvedAt: (attrs.resolved_at as string) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + retrospectives, + totalCount: retrospectives.length, + }, + } + }, + + outputs: { + retrospectives: { + type: 'array', + description: 'List of retrospectives', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique retrospective ID' }, + title: { type: 'string', description: 'Retrospective title' }, + status: { type: 'string', description: 'Status (draft or published)' }, + url: { type: 'string', description: 'URL to the retrospective' }, + startedAt: { type: 'string', description: 'Incident start date' }, + mitigatedAt: { type: 'string', description: 'Mitigation date' }, + resolvedAt: { type: 'string', description: 'Resolution date' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of retrospectives returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_services.ts b/apps/sim/tools/rootly/list_services.ts new file mode 100644 index 00000000000..59ef64deeac --- /dev/null +++ b/apps/sim/tools/rootly/list_services.ts @@ -0,0 +1,111 @@ +import type { RootlyListServicesParams, RootlyListServicesResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListServicesTool: ToolConfig< + RootlyListServicesParams, + RootlyListServicesResponse +> = { + id: 'rootly_list_services', + name: 'Rootly List Services', + description: 'List services from Rootly with optional search filtering.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + search: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter services', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.search) queryParams.set('filter[search]', params.search) + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/services${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { services: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const services = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + name: (attrs.name as string) ?? '', + slug: (attrs.slug as string) ?? null, + description: (attrs.description as string) ?? null, + color: (attrs.color as string) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + services, + totalCount: services.length, + }, + } + }, + + outputs: { + services: { + type: 'array', + description: 'List of services', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique service ID' }, + name: { type: 'string', description: 'Service name' }, + slug: { type: 'string', description: 'Service slug' }, + description: { type: 'string', description: 'Service description' }, + color: { type: 'string', description: 'Service color' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of services returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_severities.ts b/apps/sim/tools/rootly/list_severities.ts new file mode 100644 index 00000000000..b0517c03fd7 --- /dev/null +++ b/apps/sim/tools/rootly/list_severities.ts @@ -0,0 +1,108 @@ +import type { RootlyListSeveritiesParams, RootlyListSeveritiesResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListSeveritiesTool: ToolConfig< + RootlyListSeveritiesParams, + RootlyListSeveritiesResponse +> = { + id: 'rootly_list_severities', + name: 'Rootly List Severities', + description: 'List severity levels configured in Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/severities${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { severities: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const severities = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + name: (attrs.name as string) ?? '', + slug: (attrs.slug as string) ?? null, + description: (attrs.description as string) ?? null, + severity: (attrs.severity as string) ?? null, + color: (attrs.color as string) ?? null, + position: (attrs.position as number) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + severities, + totalCount: severities.length, + }, + } + }, + + outputs: { + severities: { + type: 'array', + description: 'List of severity levels', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique severity ID' }, + name: { type: 'string', description: 'Severity name' }, + slug: { type: 'string', description: 'Severity slug' }, + description: { type: 'string', description: 'Severity description' }, + severity: { type: 'string', description: 'Severity level (critical, high, medium, low)' }, + color: { type: 'string', description: 'Severity color' }, + position: { type: 'number', description: 'Display position' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of severities returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_teams.ts b/apps/sim/tools/rootly/list_teams.ts new file mode 100644 index 00000000000..78084c99f25 --- /dev/null +++ b/apps/sim/tools/rootly/list_teams.ts @@ -0,0 +1,108 @@ +import type { RootlyListTeamsParams, RootlyListTeamsResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListTeamsTool: ToolConfig = { + id: 'rootly_list_teams', + name: 'Rootly List Teams', + description: 'List teams (groups) configured in Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + search: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter teams', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.search) queryParams.set('filter[search]', params.search) + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/teams${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { teams: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const teams = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + name: (attrs.name as string) ?? '', + slug: (attrs.slug as string) ?? null, + description: (attrs.description as string) ?? null, + color: (attrs.color as string) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + teams, + totalCount: teams.length, + }, + } + }, + + outputs: { + teams: { + type: 'array', + description: 'List of teams', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique team ID' }, + name: { type: 'string', description: 'Team name' }, + slug: { type: 'string', description: 'Team slug' }, + description: { type: 'string', description: 'Team description' }, + color: { type: 'string', description: 'Team color' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of teams returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/types.ts b/apps/sim/tools/rootly/types.ts new file mode 100644 index 00000000000..05db1eb788c --- /dev/null +++ b/apps/sim/tools/rootly/types.ts @@ -0,0 +1,357 @@ +import type { ToolResponse } from '@/tools/types' + +/** Base parameters for all Rootly API operations */ +export interface RootlyBaseParams { + apiKey: string +} + +/** Create Incident */ +export interface RootlyCreateIncidentParams extends RootlyBaseParams { + title: string + summary?: string + severityId?: string + status?: string + kind?: string + serviceIds?: string + environmentIds?: string + groupIds?: string + incidentTypeIds?: string + functionalityIds?: string + labels?: string + private?: boolean +} + +export interface RootlyIncidentData { + id: string + sequentialId: number | null + title: string + slug: string | null + kind: string | null + summary: string | null + status: string | null + private: boolean + url: string | null + shortUrl: string | null + severityName: string | null + severityId: string | null + createdAt: string + updatedAt: string + startedAt: string | null + mitigatedAt: string | null + resolvedAt: string | null + closedAt: string | null +} + +export interface RootlyCreateIncidentResponse extends ToolResponse { + output: { + incident: RootlyIncidentData + } +} + +/** Get Incident */ +export interface RootlyGetIncidentParams extends RootlyBaseParams { + incidentId: string +} + +export interface RootlyGetIncidentResponse extends ToolResponse { + output: { + incident: RootlyIncidentData + } +} + +/** Update Incident */ +export interface RootlyUpdateIncidentParams extends RootlyBaseParams { + incidentId: string + title?: string + summary?: string + severityId?: string + status?: string + serviceIds?: string + environmentIds?: string + groupIds?: string + mitigationMessage?: string + resolutionMessage?: string +} + +export interface RootlyUpdateIncidentResponse extends ToolResponse { + output: { + incident: RootlyIncidentData + } +} + +/** List Incidents */ +export interface RootlyListIncidentsParams extends RootlyBaseParams { + status?: string + severity?: string + search?: string + services?: string + teams?: string + environments?: string + sort?: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyListIncidentsResponse extends ToolResponse { + output: { + incidents: RootlyIncidentData[] + totalCount: number + } +} + +/** Create Alert */ +export interface RootlyCreateAlertParams extends RootlyBaseParams { + summary: string + description?: string + source?: string + status?: string + serviceIds?: string + groupIds?: string + environmentIds?: string + externalId?: string + externalUrl?: string + deduplicationKey?: string +} + +export interface RootlyAlertData { + id: string + summary: string + description: string | null + source: string | null + status: string | null + externalId: string | null + externalUrl: string | null + deduplicationKey: string | null + createdAt: string + updatedAt: string +} + +export interface RootlyCreateAlertResponse extends ToolResponse { + output: { + alert: RootlyAlertData + } +} + +/** List Alerts */ +export interface RootlyListAlertsParams extends RootlyBaseParams { + status?: string + source?: string + services?: string + environments?: string + groups?: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyListAlertsResponse extends ToolResponse { + output: { + alerts: RootlyAlertData[] + totalCount: number + } +} + +/** Add Incident Event */ +export interface RootlyAddIncidentEventParams extends RootlyBaseParams { + incidentId: string + event: string + visibility?: string +} + +export interface RootlyAddIncidentEventResponse extends ToolResponse { + output: { + eventId: string + event: string + visibility: string | null + occurredAt: string | null + createdAt: string + } +} + +/** List Services */ +export interface RootlyListServicesParams extends RootlyBaseParams { + search?: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyServiceData { + id: string + name: string + slug: string | null + description: string | null + color: string | null + createdAt: string + updatedAt: string +} + +export interface RootlyListServicesResponse extends ToolResponse { + output: { + services: RootlyServiceData[] + totalCount: number + } +} + +/** List Severities */ +export interface RootlyListSeveritiesParams extends RootlyBaseParams { + pageSize?: number + pageNumber?: number +} + +export interface RootlySeverityData { + id: string + name: string + slug: string | null + description: string | null + severity: string | null + color: string | null + position: number | null + createdAt: string + updatedAt: string +} + +export interface RootlyListSeveritiesResponse extends ToolResponse { + output: { + severities: RootlySeverityData[] + totalCount: number + } +} + +/** List Retrospectives */ +export interface RootlyListRetrospectivesParams extends RootlyBaseParams { + status?: string + search?: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyRetrospectiveData { + id: string + title: string + status: string | null + url: string | null + startedAt: string | null + mitigatedAt: string | null + resolvedAt: string | null + createdAt: string + updatedAt: string +} + +export interface RootlyListRetrospectivesResponse extends ToolResponse { + output: { + retrospectives: RootlyRetrospectiveData[] + totalCount: number + } +} + +/** List Teams */ +export interface RootlyListTeamsParams extends RootlyBaseParams { + search?: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyTeamData { + id: string + name: string + slug: string | null + description: string | null + color: string | null + createdAt: string + updatedAt: string +} + +export interface RootlyListTeamsResponse extends ToolResponse { + output: { + teams: RootlyTeamData[] + totalCount: number + } +} + +/** List Environments */ +export interface RootlyListEnvironmentsParams extends RootlyBaseParams { + search?: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyEnvironmentData { + id: string + name: string + slug: string | null + description: string | null + color: string | null + createdAt: string + updatedAt: string +} + +export interface RootlyListEnvironmentsResponse extends ToolResponse { + output: { + environments: RootlyEnvironmentData[] + totalCount: number + } +} + +/** List Incident Types */ +export interface RootlyListIncidentTypesParams extends RootlyBaseParams { + search?: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyIncidentTypeData { + id: string + name: string + slug: string | null + description: string | null + color: string | null + createdAt: string + updatedAt: string +} + +export interface RootlyListIncidentTypesResponse extends ToolResponse { + output: { + incidentTypes: RootlyIncidentTypeData[] + totalCount: number + } +} + +/** List Functionalities */ +export interface RootlyListFunctionalitiesParams extends RootlyBaseParams { + search?: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyFunctionalityData { + id: string + name: string + slug: string | null + description: string | null + color: string | null + createdAt: string + updatedAt: string +} + +export interface RootlyListFunctionalitiesResponse extends ToolResponse { + output: { + functionalities: RootlyFunctionalityData[] + totalCount: number + } +} + +/** Union of all responses */ +export type RootlyResponse = + | RootlyCreateIncidentResponse + | RootlyGetIncidentResponse + | RootlyUpdateIncidentResponse + | RootlyListIncidentsResponse + | RootlyCreateAlertResponse + | RootlyListAlertsResponse + | RootlyAddIncidentEventResponse + | RootlyListServicesResponse + | RootlyListSeveritiesResponse + | RootlyListRetrospectivesResponse + | RootlyListTeamsResponse + | RootlyListEnvironmentsResponse + | RootlyListIncidentTypesResponse + | RootlyListFunctionalitiesResponse diff --git a/apps/sim/tools/rootly/update_incident.ts b/apps/sim/tools/rootly/update_incident.ts new file mode 100644 index 00000000000..6b12bdc2808 --- /dev/null +++ b/apps/sim/tools/rootly/update_incident.ts @@ -0,0 +1,176 @@ +import type { RootlyUpdateIncidentParams, RootlyUpdateIncidentResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyUpdateIncidentTool: ToolConfig< + RootlyUpdateIncidentParams, + RootlyUpdateIncidentResponse +> = { + id: 'rootly_update_incident', + name: 'Rootly Update Incident', + description: 'Update an existing incident in Rootly (status, severity, summary, etc.).', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + incidentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the incident to update', + }, + title: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Updated incident title', + }, + summary: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Updated incident summary', + }, + severityId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Updated severity ID', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Updated status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled)', + }, + serviceIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated service IDs', + }, + environmentIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated environment IDs', + }, + groupIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated team/group IDs', + }, + mitigationMessage: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'How was the incident mitigated?', + }, + resolutionMessage: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'How was the incident resolved?', + }, + }, + + request: { + url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}`, + method: 'PUT', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + body: (params) => { + const attributes: Record = {} + if (params.title) attributes.title = params.title + if (params.summary) attributes.summary = params.summary + if (params.severityId) attributes.severity_id = params.severityId + if (params.status) attributes.status = params.status + if (params.mitigationMessage) attributes.mitigation_message = params.mitigationMessage + if (params.resolutionMessage) attributes.resolution_message = params.resolutionMessage + if (params.serviceIds) { + attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim()) + } + if (params.environmentIds) { + attributes.environment_ids = params.environmentIds.split(',').map((s: string) => s.trim()) + } + if (params.groupIds) { + attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim()) + } + return { data: { type: 'incidents', attributes } } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { incident: {} as RootlyUpdateIncidentResponse['output']['incident'] }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const attrs = data.data?.attributes || {} + return { + success: true, + output: { + incident: { + id: data.data?.id ?? null, + sequentialId: attrs.sequential_id ?? null, + title: attrs.title ?? '', + slug: attrs.slug ?? null, + kind: attrs.kind ?? null, + summary: attrs.summary ?? null, + status: attrs.status ?? null, + private: attrs.private ?? false, + url: attrs.url ?? null, + shortUrl: attrs.short_url ?? null, + severityName: attrs.severity?.data?.attributes?.name ?? null, + severityId: attrs.severity?.data?.id ?? null, + createdAt: attrs.created_at ?? '', + updatedAt: attrs.updated_at ?? '', + startedAt: attrs.started_at ?? null, + mitigatedAt: attrs.mitigated_at ?? null, + resolvedAt: attrs.resolved_at ?? null, + closedAt: attrs.closed_at ?? null, + }, + }, + } + }, + + outputs: { + incident: { + type: 'object', + description: 'The updated incident', + properties: { + id: { type: 'string', description: 'Unique incident ID' }, + sequentialId: { type: 'number', description: 'Sequential incident number' }, + title: { type: 'string', description: 'Incident title' }, + slug: { type: 'string', description: 'Incident slug' }, + kind: { type: 'string', description: 'Incident kind' }, + summary: { type: 'string', description: 'Incident summary' }, + status: { type: 'string', description: 'Incident status' }, + private: { type: 'boolean', description: 'Whether the incident is private' }, + url: { type: 'string', description: 'URL to the incident' }, + shortUrl: { type: 'string', description: 'Short URL to the incident' }, + severityName: { type: 'string', description: 'Severity name' }, + severityId: { type: 'string', description: 'Severity ID' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + startedAt: { type: 'string', description: 'Start date' }, + mitigatedAt: { type: 'string', description: 'Mitigation date' }, + resolvedAt: { type: 'string', description: 'Resolution date' }, + closedAt: { type: 'string', description: 'Closed date' }, + }, + }, + }, +} From a61518bf7ae97e5aaea916c7ff6305125e4e6383 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Thu, 2 Apr 2026 10:41:44 -0700 Subject: [PATCH 2/8] fix(rootly): address PR review feedback - PATCH method, totalCount, environmentIds - Changed update_incident HTTP method from PUT to PATCH per Rootly API spec - Fixed totalCount in all 9 list tools to use data.meta?.total_count from API response - Added missing updateEnvironmentIds subBlock and params mapping for update_incident --- apps/sim/blocks/blocks/rootly.ts | 10 ++++++++++ apps/sim/tools/rootly/list_alerts.ts | 2 +- apps/sim/tools/rootly/list_environments.ts | 2 +- apps/sim/tools/rootly/list_functionalities.ts | 2 +- apps/sim/tools/rootly/list_incident_types.ts | 2 +- apps/sim/tools/rootly/list_incidents.ts | 2 +- apps/sim/tools/rootly/list_retrospectives.ts | 2 +- apps/sim/tools/rootly/list_services.ts | 2 +- apps/sim/tools/rootly/list_severities.ts | 2 +- apps/sim/tools/rootly/list_teams.ts | 2 +- apps/sim/tools/rootly/update_incident.ts | 2 +- 11 files changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/sim/blocks/blocks/rootly.ts b/apps/sim/blocks/blocks/rootly.ts index 36efaa9a878..ad10efdc054 100644 --- a/apps/sim/blocks/blocks/rootly.ts +++ b/apps/sim/blocks/blocks/rootly.ts @@ -232,6 +232,14 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_update_incident' }, mode: 'advanced', }, + { + id: 'updateEnvironmentIds', + title: 'Environment IDs', + type: 'short-input', + placeholder: 'Comma-separated environment IDs', + condition: { field: 'operation', value: 'rootly_update_incident' }, + mode: 'advanced', + }, { id: 'updateGroupIds', title: 'Team IDs', @@ -612,6 +620,7 @@ export const RootlyBlock: BlockConfig = { mitigationMessage: params.mitigationMessage, resolutionMessage: params.resolutionMessage, serviceIds: params.updateServiceIds, + environmentIds: params.updateEnvironmentIds, groupIds: params.updateGroupIds, } @@ -742,6 +751,7 @@ export const RootlyBlock: BlockConfig = { mitigationMessage: { type: 'string', description: 'Mitigation message' }, resolutionMessage: { type: 'string', description: 'Resolution message' }, updateServiceIds: { type: 'string', description: 'Updated service IDs' }, + updateEnvironmentIds: { type: 'string', description: 'Updated environment IDs' }, updateGroupIds: { type: 'string', description: 'Updated team IDs' }, listIncidentsStatus: { type: 'string', description: 'Filter by status' }, listIncidentsSearch: { type: 'string', description: 'Search incidents' }, diff --git a/apps/sim/tools/rootly/list_alerts.ts b/apps/sim/tools/rootly/list_alerts.ts index cc40bfbeb33..67527994962 100644 --- a/apps/sim/tools/rootly/list_alerts.ts +++ b/apps/sim/tools/rootly/list_alerts.ts @@ -109,7 +109,7 @@ export const rootlyListAlertsTool: ToolConfig `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}`, - method: 'PUT', + method: 'PATCH', headers: (params) => ({ 'Content-Type': 'application/vnd.api+json', Authorization: `Bearer ${params.apiKey}`, From d3491dbfda449b932f4b033f2c075f14b170ec47 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Thu, 2 Apr 2026 10:52:27 -0700 Subject: [PATCH 3/8] fix(rootly): add id to PATCH body and unchanged option to update status dropdown - Include incident id in JSON:API PATCH body per spec requirement - Add 'Unchanged' empty option to updateStatus dropdown to avoid accidental overwrites --- apps/sim/blocks/blocks/rootly.ts | 1 + apps/sim/tools/rootly/update_incident.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/sim/blocks/blocks/rootly.ts b/apps/sim/blocks/blocks/rootly.ts index ad10efdc054..54d0b92319a 100644 --- a/apps/sim/blocks/blocks/rootly.ts +++ b/apps/sim/blocks/blocks/rootly.ts @@ -189,6 +189,7 @@ export const RootlyBlock: BlockConfig = { title: 'Status', type: 'dropdown', options: [ + { label: 'Unchanged', id: '' }, { label: 'In Triage', id: 'in_triage' }, { label: 'Started', id: 'started' }, { label: 'Detected', id: 'detected' }, diff --git a/apps/sim/tools/rootly/update_incident.ts b/apps/sim/tools/rootly/update_incident.ts index 907f58b8862..a42e2566713 100644 --- a/apps/sim/tools/rootly/update_incident.ts +++ b/apps/sim/tools/rootly/update_incident.ts @@ -104,7 +104,7 @@ export const rootlyUpdateIncidentTool: ToolConfig< if (params.groupIds) { attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim()) } - return { data: { type: 'incidents', attributes } } + return { data: { type: 'incidents', id: params.incidentId.trim(), attributes } } }, }, From bb429e739a8b0e0018f302b99a3fd1855682400d Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Thu, 2 Apr 2026 10:58:22 -0700 Subject: [PATCH 4/8] icon update --- apps/docs/components/icons.tsx | 15 +++++++-------- apps/sim/components/icons.tsx | 15 +++++++-------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 35cf31a2ce4..4662b1c0d3f 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -6390,34 +6390,33 @@ export function RipplingIcon(props: SVGProps) { export function RootlyIcon(props: SVGProps) { return ( - ) diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 35cf31a2ce4..4662b1c0d3f 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -6390,34 +6390,33 @@ export function RipplingIcon(props: SVGProps) { export function RootlyIcon(props: SVGProps) { return ( - ) From 4745f4450389085e529616d85a6bb5d807513cb0 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Thu, 2 Apr 2026 11:08:06 -0700 Subject: [PATCH 5/8] improvement(rootly): complete block-tool alignment and fix validation gaps - Add missing get_incident output fields (private, shortUrl, closedAt) - Add missing block subBlocks: createPrivate, alertStatus, alertExternalId, listAlertsServices - Add pageNumber subBlocks for all 9 list operations - Add teams/environments filter subBlocks for list_incidents and list_alerts - Add environmentIds subBlock for create_alert - Add empty default options to all optional dropdowns (createStatus, createKind, listIncidentsSort, eventVisibility) - Wire all new subBlocks in tools.config.params and inputs - Regenerate docs --- apps/docs/content/docs/en/tools/rootly.mdx | 7 +- apps/sim/blocks/blocks/rootly.ts | 214 +++++++++++++++++++++ apps/sim/tools/rootly/get_incident.ts | 7 +- 3 files changed, 224 insertions(+), 4 deletions(-) diff --git a/apps/docs/content/docs/en/tools/rootly.mdx b/apps/docs/content/docs/en/tools/rootly.mdx index 8288a1dee45..473a405fd0b 100644 --- a/apps/docs/content/docs/en/tools/rootly.mdx +++ b/apps/docs/content/docs/en/tools/rootly.mdx @@ -113,14 +113,17 @@ Retrieve a single incident by ID from Rootly. | ↳ `kind` | string | Incident kind | | ↳ `summary` | string | Incident summary | | ↳ `status` | string | Incident status | +| ↳ `private` | boolean | Whether the incident is private | | ↳ `url` | string | URL to the incident | +| ↳ `shortUrl` | string | Short URL to the incident | | ↳ `severityName` | string | Severity name | | ↳ `severityId` | string | Severity ID | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | | ↳ `startedAt` | string | Start date | | ↳ `mitigatedAt` | string | Mitigation date | | ↳ `resolvedAt` | string | Resolution date | -| ↳ `createdAt` | string | Creation date | -| ↳ `updatedAt` | string | Last update date | +| ↳ `closedAt` | string | Closed date | ### `rootly_update_incident` diff --git a/apps/sim/blocks/blocks/rootly.ts b/apps/sim/blocks/blocks/rootly.ts index 54d0b92319a..70eea093f65 100644 --- a/apps/sim/blocks/blocks/rootly.ts +++ b/apps/sim/blocks/blocks/rootly.ts @@ -69,6 +69,7 @@ export const RootlyBlock: BlockConfig = { title: 'Status', type: 'dropdown', options: [ + { label: 'Default', id: '' }, { label: 'In Triage', id: 'in_triage' }, { label: 'Started', id: 'started' }, { label: 'Detected', id: 'detected' }, @@ -78,6 +79,7 @@ export const RootlyBlock: BlockConfig = { { label: 'Closed', id: 'closed' }, { label: 'Cancelled', id: 'cancelled' }, ], + value: () => '', condition: { field: 'operation', value: 'rootly_create_incident' }, mode: 'advanced', }, @@ -86,12 +88,14 @@ export const RootlyBlock: BlockConfig = { title: 'Kind', type: 'dropdown', options: [ + { label: 'Default', id: '' }, { label: 'Normal', id: 'normal' }, { label: 'Test', id: 'test' }, { label: 'Example', id: 'example' }, { label: 'Backfilled', id: 'backfilled' }, { label: 'Scheduled', id: 'scheduled' }, ], + value: () => '', condition: { field: 'operation', value: 'rootly_create_incident' }, mode: 'advanced', }, @@ -150,6 +154,19 @@ export const RootlyBlock: BlockConfig = { generationType: 'json-object', }, }, + { + id: 'createPrivate', + title: 'Private', + type: 'dropdown', + options: [ + { label: 'Default', id: '' }, + { label: 'Yes', id: 'true' }, + { label: 'No', id: 'false' }, + ], + value: () => '', + condition: { field: 'operation', value: 'rootly_create_incident' }, + mode: 'advanced', + }, // Get Incident fields { @@ -296,11 +313,29 @@ export const RootlyBlock: BlockConfig = { title: 'Sort', type: 'dropdown', options: [ + { label: 'Default', id: '' }, { label: 'Newest First', id: '-created_at' }, { label: 'Oldest First', id: 'created_at' }, { label: 'Recently Started', id: '-started_at' }, { label: 'Recently Updated', id: '-updated_at' }, ], + value: () => '', + condition: { field: 'operation', value: 'rootly_list_incidents' }, + mode: 'advanced', + }, + { + id: 'listIncidentsTeams', + title: 'Teams Filter', + type: 'short-input', + placeholder: 'Comma-separated team slugs', + condition: { field: 'operation', value: 'rootly_list_incidents' }, + mode: 'advanced', + }, + { + id: 'listIncidentsEnvironments', + title: 'Environments Filter', + type: 'short-input', + placeholder: 'Comma-separated environment slugs', condition: { field: 'operation', value: 'rootly_list_incidents' }, mode: 'advanced', }, @@ -312,6 +347,14 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_list_incidents' }, mode: 'advanced', }, + { + id: 'listIncidentsPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_incidents' }, + mode: 'advanced', + }, // Create Alert fields { @@ -361,6 +404,37 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_create_alert' }, mode: 'advanced', }, + { + id: 'alertEnvironmentIds', + title: 'Environment IDs', + type: 'short-input', + placeholder: 'Comma-separated environment IDs', + condition: { field: 'operation', value: 'rootly_create_alert' }, + mode: 'advanced', + }, + { + id: 'alertStatus', + title: 'Status', + type: 'dropdown', + options: [ + { label: 'Default', id: '' }, + { label: 'Open', id: 'open' }, + { label: 'Triggered', id: 'triggered' }, + { label: 'Acknowledged', id: 'acknowledged' }, + { label: 'Resolved', id: 'resolved' }, + ], + value: () => '', + condition: { field: 'operation', value: 'rootly_create_alert' }, + mode: 'advanced', + }, + { + id: 'alertExternalId', + title: 'External ID', + type: 'short-input', + placeholder: 'External alert ID', + condition: { field: 'operation', value: 'rootly_create_alert' }, + mode: 'advanced', + }, { id: 'alertExternalUrl', title: 'External URL', @@ -392,6 +466,30 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_list_alerts' }, mode: 'advanced', }, + { + id: 'listAlertsServices', + title: 'Services Filter', + type: 'short-input', + placeholder: 'Comma-separated service slugs', + condition: { field: 'operation', value: 'rootly_list_alerts' }, + mode: 'advanced', + }, + { + id: 'listAlertsEnvironments', + title: 'Environments Filter', + type: 'short-input', + placeholder: 'Comma-separated environment slugs', + condition: { field: 'operation', value: 'rootly_list_alerts' }, + mode: 'advanced', + }, + { + id: 'listAlertsGroups', + title: 'Teams Filter', + type: 'short-input', + placeholder: 'Comma-separated team slugs', + condition: { field: 'operation', value: 'rootly_list_alerts' }, + mode: 'advanced', + }, { id: 'listAlertsPageSize', title: 'Page Size', @@ -400,6 +498,14 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_list_alerts' }, mode: 'advanced', }, + { + id: 'listAlertsPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_alerts' }, + mode: 'advanced', + }, // Add Incident Event fields { @@ -423,9 +529,11 @@ export const RootlyBlock: BlockConfig = { title: 'Visibility', type: 'dropdown', options: [ + { label: 'Default', id: '' }, { label: 'Internal', id: 'internal' }, { label: 'External', id: 'external' }, ], + value: () => '', condition: { field: 'operation', value: 'rootly_add_incident_event' }, mode: 'advanced', }, @@ -446,6 +554,14 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_list_services' }, mode: 'advanced', }, + { + id: 'servicesPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_services' }, + mode: 'advanced', + }, // List Severities fields { @@ -456,6 +572,14 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_list_severities' }, mode: 'advanced', }, + { + id: 'severitiesPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_severities' }, + mode: 'advanced', + }, // List Teams fields { @@ -473,6 +597,14 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_list_teams' }, mode: 'advanced', }, + { + id: 'teamsPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_teams' }, + mode: 'advanced', + }, // List Environments fields { @@ -490,6 +622,14 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_list_environments' }, mode: 'advanced', }, + { + id: 'environmentsPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_environments' }, + mode: 'advanced', + }, // List Incident Types fields { @@ -507,6 +647,14 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_list_incident_types' }, mode: 'advanced', }, + { + id: 'incidentTypesPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_incident_types' }, + mode: 'advanced', + }, // List Functionalities fields { @@ -524,6 +672,14 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_list_functionalities' }, mode: 'advanced', }, + { + id: 'functionalitiesPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_functionalities' }, + mode: 'advanced', + }, // List Retrospectives fields { @@ -552,6 +708,14 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_list_retrospectives' }, mode: 'advanced', }, + { + id: 'retrospectivesPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_retrospectives' }, + mode: 'advanced', + }, // API Key (common) { @@ -602,6 +766,7 @@ export const RootlyBlock: BlockConfig = { incidentTypeIds: params.createIncidentTypeIds, functionalityIds: params.createFunctionalityIds, labels: params.createLabels, + private: params.createPrivate ? params.createPrivate === 'true' : undefined, } case 'rootly_get_incident': @@ -632,10 +797,15 @@ export const RootlyBlock: BlockConfig = { search: params.listIncidentsSearch, severity: params.listIncidentsSeverity, services: params.listIncidentsServices, + teams: params.listIncidentsTeams, + environments: params.listIncidentsEnvironments, sort: params.listIncidentsSort, pageSize: params.listIncidentsPageSize ? Number(params.listIncidentsPageSize) : undefined, + pageNumber: params.listIncidentsPageNumber + ? Number(params.listIncidentsPageNumber) + : undefined, } case 'rootly_create_alert': @@ -644,8 +814,11 @@ export const RootlyBlock: BlockConfig = { summary: params.alertSummary, description: params.alertDescription, source: params.alertSource, + status: params.alertStatus, serviceIds: params.alertServiceIds, groupIds: params.alertGroupIds, + environmentIds: params.alertEnvironmentIds, + externalId: params.alertExternalId, deduplicationKey: params.alertDeduplicationKey, externalUrl: params.alertExternalUrl, } @@ -655,7 +828,13 @@ export const RootlyBlock: BlockConfig = { ...baseParams, status: params.listAlertsStatus, source: params.listAlertsSource, + services: params.listAlertsServices, + environments: params.listAlertsEnvironments, + groups: params.listAlertsGroups, pageSize: params.listAlertsPageSize ? Number(params.listAlertsPageSize) : undefined, + pageNumber: params.listAlertsPageNumber + ? Number(params.listAlertsPageNumber) + : undefined, } case 'rootly_add_incident_event': @@ -671,12 +850,16 @@ export const RootlyBlock: BlockConfig = { ...baseParams, search: params.servicesSearch, pageSize: params.servicesPageSize ? Number(params.servicesPageSize) : undefined, + pageNumber: params.servicesPageNumber ? Number(params.servicesPageNumber) : undefined, } case 'rootly_list_severities': return { ...baseParams, pageSize: params.severitiesPageSize ? Number(params.severitiesPageSize) : undefined, + pageNumber: params.severitiesPageNumber + ? Number(params.severitiesPageNumber) + : undefined, } case 'rootly_list_teams': @@ -684,6 +867,7 @@ export const RootlyBlock: BlockConfig = { ...baseParams, search: params.teamsSearch, pageSize: params.teamsPageSize ? Number(params.teamsPageSize) : undefined, + pageNumber: params.teamsPageNumber ? Number(params.teamsPageNumber) : undefined, } case 'rootly_list_environments': @@ -693,6 +877,9 @@ export const RootlyBlock: BlockConfig = { pageSize: params.environmentsPageSize ? Number(params.environmentsPageSize) : undefined, + pageNumber: params.environmentsPageNumber + ? Number(params.environmentsPageNumber) + : undefined, } case 'rootly_list_incident_types': @@ -702,6 +889,9 @@ export const RootlyBlock: BlockConfig = { pageSize: params.incidentTypesPageSize ? Number(params.incidentTypesPageSize) : undefined, + pageNumber: params.incidentTypesPageNumber + ? Number(params.incidentTypesPageNumber) + : undefined, } case 'rootly_list_functionalities': @@ -711,6 +901,9 @@ export const RootlyBlock: BlockConfig = { pageSize: params.functionalitiesPageSize ? Number(params.functionalitiesPageSize) : undefined, + pageNumber: params.functionalitiesPageNumber + ? Number(params.functionalitiesPageNumber) + : undefined, } case 'rootly_list_retrospectives': @@ -721,6 +914,9 @@ export const RootlyBlock: BlockConfig = { pageSize: params.retrospectivesPageSize ? Number(params.retrospectivesPageSize) : undefined, + pageNumber: params.retrospectivesPageNumber + ? Number(params.retrospectivesPageNumber) + : undefined, } default: @@ -743,6 +939,7 @@ export const RootlyBlock: BlockConfig = { createIncidentTypeIds: { type: 'string', description: 'Incident type IDs' }, createFunctionalityIds: { type: 'string', description: 'Functionality IDs' }, createLabels: { type: 'string', description: 'Labels as JSON' }, + createPrivate: { type: 'string', description: 'Whether incident is private' }, getIncidentId: { type: 'string', description: 'Incident ID to retrieve' }, updateIncidentId: { type: 'string', description: 'Incident ID to update' }, updateTitle: { type: 'string', description: 'Updated title' }, @@ -758,35 +955,52 @@ export const RootlyBlock: BlockConfig = { listIncidentsSearch: { type: 'string', description: 'Search incidents' }, listIncidentsSeverity: { type: 'string', description: 'Filter by severity' }, listIncidentsServices: { type: 'string', description: 'Filter by services' }, + listIncidentsTeams: { type: 'string', description: 'Filter by teams' }, + listIncidentsEnvironments: { type: 'string', description: 'Filter by environments' }, listIncidentsSort: { type: 'string', description: 'Sort order' }, listIncidentsPageSize: { type: 'string', description: 'Page size' }, + listIncidentsPageNumber: { type: 'string', description: 'Page number' }, alertSummary: { type: 'string', description: 'Alert summary' }, alertDescription: { type: 'string', description: 'Alert description' }, alertSource: { type: 'string', description: 'Alert source' }, alertServiceIds: { type: 'string', description: 'Alert service IDs' }, alertGroupIds: { type: 'string', description: 'Alert team IDs' }, + alertEnvironmentIds: { type: 'string', description: 'Alert environment IDs' }, + alertStatus: { type: 'string', description: 'Alert status' }, + alertExternalId: { type: 'string', description: 'External alert ID' }, alertDeduplicationKey: { type: 'string', description: 'Deduplication key' }, alertExternalUrl: { type: 'string', description: 'External URL' }, listAlertsStatus: { type: 'string', description: 'Filter alerts by status' }, listAlertsSource: { type: 'string', description: 'Filter alerts by source' }, + listAlertsServices: { type: 'string', description: 'Filter alerts by services' }, + listAlertsEnvironments: { type: 'string', description: 'Filter alerts by environments' }, + listAlertsGroups: { type: 'string', description: 'Filter alerts by teams' }, listAlertsPageSize: { type: 'string', description: 'Alerts page size' }, + listAlertsPageNumber: { type: 'string', description: 'Alerts page number' }, eventIncidentId: { type: 'string', description: 'Incident ID for event' }, eventText: { type: 'string', description: 'Event description' }, eventVisibility: { type: 'string', description: 'Event visibility' }, servicesSearch: { type: 'string', description: 'Search services' }, servicesPageSize: { type: 'string', description: 'Services page size' }, + servicesPageNumber: { type: 'string', description: 'Services page number' }, severitiesPageSize: { type: 'string', description: 'Severities page size' }, + severitiesPageNumber: { type: 'string', description: 'Severities page number' }, teamsSearch: { type: 'string', description: 'Search teams' }, teamsPageSize: { type: 'string', description: 'Teams page size' }, + teamsPageNumber: { type: 'string', description: 'Teams page number' }, environmentsSearch: { type: 'string', description: 'Search environments' }, environmentsPageSize: { type: 'string', description: 'Environments page size' }, + environmentsPageNumber: { type: 'string', description: 'Environments page number' }, incidentTypesSearch: { type: 'string', description: 'Search incident types' }, incidentTypesPageSize: { type: 'string', description: 'Incident types page size' }, + incidentTypesPageNumber: { type: 'string', description: 'Incident types page number' }, functionalitiesSearch: { type: 'string', description: 'Search functionalities' }, functionalitiesPageSize: { type: 'string', description: 'Functionalities page size' }, + functionalitiesPageNumber: { type: 'string', description: 'Functionalities page number' }, retrospectivesStatus: { type: 'string', description: 'Filter retrospectives by status' }, retrospectivesSearch: { type: 'string', description: 'Search retrospectives' }, retrospectivesPageSize: { type: 'string', description: 'Retrospectives page size' }, + retrospectivesPageNumber: { type: 'string', description: 'Retrospectives page number' }, }, outputs: { incident: { diff --git a/apps/sim/tools/rootly/get_incident.ts b/apps/sim/tools/rootly/get_incident.ts index cb3b8eaba15..70b4f6c0f6d 100644 --- a/apps/sim/tools/rootly/get_incident.ts +++ b/apps/sim/tools/rootly/get_incident.ts @@ -83,14 +83,17 @@ export const rootlyGetIncidentTool: ToolConfig Date: Thu, 2 Apr 2026 11:23:56 -0700 Subject: [PATCH 6/8] fix(rootly): align tools with OpenAPI spec - list_incident_types: use filter[name] instead of unsupported filter[search] - list_severities: add missing search param (filter[search]) - create_incident: title is optional per API (auto-generated if null) - update_incident: add kind, private, labels, incidentTypeIds, functionalityIds, cancellationMessage params - create/update/list incidents: add scheduled, in_progress, completed status values - create_alert: fix status description (only open/triggered on create) - add_incident_event: add updatedAt to response - block: add matching subBlocks and params for all new tool fields Co-Authored-By: Claude Opus 4.6 --- apps/docs/content/docs/en/tools/rootly.mdx | 24 ++-- apps/sim/blocks/blocks/rootly.ts | 109 ++++++++++++++++++- apps/sim/tools/rootly/add_incident_event.ts | 6 + apps/sim/tools/rootly/create_alert.ts | 6 +- apps/sim/tools/rootly/create_incident.ts | 14 +-- apps/sim/tools/rootly/list_incident_types.ts | 4 +- apps/sim/tools/rootly/list_incidents.ts | 2 +- apps/sim/tools/rootly/list_severities.ts | 7 ++ apps/sim/tools/rootly/types.ts | 12 +- apps/sim/tools/rootly/update_incident.ts | 59 +++++++++- 10 files changed, 213 insertions(+), 30 deletions(-) diff --git a/apps/docs/content/docs/en/tools/rootly.mdx b/apps/docs/content/docs/en/tools/rootly.mdx index 473a405fd0b..58b3cb1e907 100644 --- a/apps/docs/content/docs/en/tools/rootly.mdx +++ b/apps/docs/content/docs/en/tools/rootly.mdx @@ -53,11 +53,11 @@ Create a new incident in Rootly with optional severity, services, and teams. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rootly API key | -| `title` | string | Yes | The title of the incident | +| `title` | string | No | The title of the incident \(auto-generated if not provided\) | | `summary` | string | No | A summary of the incident | | `severityId` | string | No | Severity ID to attach to the incident | -| `status` | string | No | Incident status \(in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled\) | -| `kind` | string | No | Incident kind \(normal, test, example, backfilled, scheduled\) | +| `status` | string | No | Incident status \(in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed\) | +| `kind` | string | No | Incident kind \(normal, normal_sub, test, test_sub, example, example_sub, backfilled, scheduled, scheduled_sub\) | | `serviceIds` | string | No | Comma-separated service IDs to attach | | `environmentIds` | string | No | Comma-separated environment IDs to attach | | `groupIds` | string | No | Comma-separated team/group IDs to attach | @@ -138,12 +138,18 @@ Update an existing incident in Rootly (status, severity, summary, etc.). | `title` | string | No | Updated incident title | | `summary` | string | No | Updated incident summary | | `severityId` | string | No | Updated severity ID | -| `status` | string | No | Updated status \(in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled\) | +| `status` | string | No | Updated status \(in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed\) | +| `kind` | string | No | Incident kind \(normal, normal_sub, test, test_sub, example, example_sub, backfilled, scheduled, scheduled_sub\) | +| `private` | boolean | No | Set incident as private \(cannot be undone\) | | `serviceIds` | string | No | Comma-separated service IDs | | `environmentIds` | string | No | Comma-separated environment IDs | | `groupIds` | string | No | Comma-separated team/group IDs | +| `incidentTypeIds` | string | No | Comma-separated incident type IDs to attach | +| `functionalityIds` | string | No | Comma-separated functionality IDs to attach | +| `labels` | string | No | Labels as JSON object, e.g. \{"platform":"osx","version":"1.29"\} | | `mitigationMessage` | string | No | How was the incident mitigated? | | `resolutionMessage` | string | No | How was the incident resolved? | +| `cancellationMessage` | string | No | Why was the incident cancelled? | #### Output @@ -178,7 +184,7 @@ List incidents from Rootly with optional filtering by status, severity, and more | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rootly API key | -| `status` | string | No | Filter by status \(in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled\) | +| `status` | string | No | Filter by status \(in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed\) | | `severity` | string | No | Filter by severity slug | | `search` | string | No | Search term to filter incidents | | `services` | string | No | Filter by service slugs \(comma-separated\) | @@ -224,8 +230,8 @@ Create a new alert in Rootly for on-call notification and routing. | `apiKey` | string | Yes | Rootly API key | | `summary` | string | Yes | The summary of the alert | | `description` | string | No | A detailed description of the alert | -| `source` | string | No | The source of the alert \(e.g., api, manual, datadog, pagerduty\) | -| `status` | string | No | Alert status \(open, triggered, acknowledged, resolved\) | +| `source` | string | Yes | The source of the alert \(e.g., api, manual, datadog, pagerduty\) | +| `status` | string | No | Alert status on creation \(open, triggered\) | | `serviceIds` | string | No | Comma-separated service IDs to attach | | `groupIds` | string | No | Comma-separated team/group IDs to attach | | `environmentIds` | string | No | Comma-separated environment IDs to attach | @@ -305,6 +311,7 @@ Add a timeline event to an existing incident in Rootly. | `visibility` | string | Event visibility \(internal or external\) | | `occurredAt` | string | When the event occurred | | `createdAt` | string | Creation date | +| `updatedAt` | string | Last update date | ### `rootly_list_services` @@ -342,6 +349,7 @@ List severity levels configured in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rootly API key | +| `search` | string | No | Search term to filter severities | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | @@ -424,7 +432,7 @@ List incident types configured in Rootly. | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `apiKey` | string | Yes | Rootly API key | -| `search` | string | No | Search term to filter incident types | +| `search` | string | No | Filter incident types by name | | `pageSize` | number | No | Number of items per page \(default: 20\) | | `pageNumber` | number | No | Page number for pagination | diff --git a/apps/sim/blocks/blocks/rootly.ts b/apps/sim/blocks/blocks/rootly.ts index 70eea093f65..7920f114564 100644 --- a/apps/sim/blocks/blocks/rootly.ts +++ b/apps/sim/blocks/blocks/rootly.ts @@ -47,7 +47,6 @@ export const RootlyBlock: BlockConfig = { type: 'short-input', placeholder: 'Incident title', condition: { field: 'operation', value: 'rootly_create_incident' }, - required: { field: 'operation', value: 'rootly_create_incident' }, }, { id: 'createSummary', @@ -78,6 +77,9 @@ export const RootlyBlock: BlockConfig = { { label: 'Resolved', id: 'resolved' }, { label: 'Closed', id: 'closed' }, { label: 'Cancelled', id: 'cancelled' }, + { label: 'Scheduled', id: 'scheduled' }, + { label: 'In Progress', id: 'in_progress' }, + { label: 'Completed', id: 'completed' }, ], value: () => '', condition: { field: 'operation', value: 'rootly_create_incident' }, @@ -215,6 +217,9 @@ export const RootlyBlock: BlockConfig = { { label: 'Resolved', id: 'resolved' }, { label: 'Closed', id: 'closed' }, { label: 'Cancelled', id: 'cancelled' }, + { label: 'Scheduled', id: 'scheduled' }, + { label: 'In Progress', id: 'in_progress' }, + { label: 'Completed', id: 'completed' }, ], condition: { field: 'operation', value: 'rootly_update_incident' }, }, @@ -266,6 +271,74 @@ export const RootlyBlock: BlockConfig = { condition: { field: 'operation', value: 'rootly_update_incident' }, mode: 'advanced', }, + { + id: 'updateKind', + title: 'Kind', + type: 'dropdown', + options: [ + { label: 'Unchanged', id: '' }, + { label: 'Normal', id: 'normal' }, + { label: 'Test', id: 'test' }, + { label: 'Example', id: 'example' }, + { label: 'Backfilled', id: 'backfilled' }, + { label: 'Scheduled', id: 'scheduled' }, + ], + value: () => '', + condition: { field: 'operation', value: 'rootly_update_incident' }, + mode: 'advanced', + }, + { + id: 'updatePrivate', + title: 'Private', + type: 'dropdown', + options: [ + { label: 'Unchanged', id: '' }, + { label: 'Yes', id: 'true' }, + { label: 'No', id: 'false' }, + ], + value: () => '', + condition: { field: 'operation', value: 'rootly_update_incident' }, + mode: 'advanced', + }, + { + id: 'updateIncidentTypeIds', + title: 'Incident Type IDs', + type: 'short-input', + placeholder: 'Comma-separated incident type IDs', + condition: { field: 'operation', value: 'rootly_update_incident' }, + mode: 'advanced', + }, + { + id: 'updateFunctionalityIds', + title: 'Functionality IDs', + type: 'short-input', + placeholder: 'Comma-separated functionality IDs', + condition: { field: 'operation', value: 'rootly_update_incident' }, + mode: 'advanced', + }, + { + id: 'updateLabels', + title: 'Labels', + type: 'short-input', + placeholder: '{"platform":"osx","version":"1.29"}', + condition: { field: 'operation', value: 'rootly_update_incident' }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: + 'Generate a JSON object of key-value label pairs for a Rootly incident. Example: {"platform":"osx","version":"1.29","region":"us-east-1"}. Return ONLY the JSON object - no explanations, no extra text.', + placeholder: 'Describe the labels (e.g., "platform osx, version 1.29")...', + generationType: 'json-object', + }, + }, + { + id: 'cancellationMessage', + title: 'Cancellation Message', + type: 'long-input', + placeholder: 'Why was the incident cancelled?', + condition: { field: 'operation', value: 'rootly_update_incident' }, + mode: 'advanced', + }, // List Incidents fields { @@ -282,6 +355,9 @@ export const RootlyBlock: BlockConfig = { { label: 'Resolved', id: 'resolved' }, { label: 'Closed', id: 'closed' }, { label: 'Cancelled', id: 'cancelled' }, + { label: 'Scheduled', id: 'scheduled' }, + { label: 'In Progress', id: 'in_progress' }, + { label: 'Completed', id: 'completed' }, ], condition: { field: 'operation', value: 'rootly_list_incidents' }, }, @@ -378,7 +454,7 @@ export const RootlyBlock: BlockConfig = { type: 'short-input', placeholder: 'Alert source (e.g., api, datadog)', condition: { field: 'operation', value: 'rootly_create_alert' }, - mode: 'advanced', + required: { field: 'operation', value: 'rootly_create_alert' }, }, { id: 'alertServiceIds', @@ -420,8 +496,6 @@ export const RootlyBlock: BlockConfig = { { label: 'Default', id: '' }, { label: 'Open', id: 'open' }, { label: 'Triggered', id: 'triggered' }, - { label: 'Acknowledged', id: 'acknowledged' }, - { label: 'Resolved', id: 'resolved' }, ], value: () => '', condition: { field: 'operation', value: 'rootly_create_alert' }, @@ -455,6 +529,7 @@ export const RootlyBlock: BlockConfig = { { label: 'Triggered', id: 'triggered' }, { label: 'Acknowledged', id: 'acknowledged' }, { label: 'Resolved', id: 'resolved' }, + { label: 'Deferred', id: 'deferred' }, ], condition: { field: 'operation', value: 'rootly_list_alerts' }, }, @@ -564,6 +639,13 @@ export const RootlyBlock: BlockConfig = { }, // List Severities fields + { + id: 'severitiesSearch', + title: 'Search', + type: 'short-input', + placeholder: 'Search severities...', + condition: { field: 'operation', value: 'rootly_list_severities' }, + }, { id: 'severitiesPageSize', title: 'Page Size', @@ -634,9 +716,9 @@ export const RootlyBlock: BlockConfig = { // List Incident Types fields { id: 'incidentTypesSearch', - title: 'Search', + title: 'Name Filter', type: 'short-input', - placeholder: 'Search incident types...', + placeholder: 'Filter by name...', condition: { field: 'operation', value: 'rootly_list_incident_types' }, }, { @@ -783,11 +865,17 @@ export const RootlyBlock: BlockConfig = { summary: params.updateSummary, status: params.updateStatus, severityId: params.updateSeverityId, + kind: params.updateKind, + private: params.updatePrivate ? params.updatePrivate === 'true' : undefined, mitigationMessage: params.mitigationMessage, resolutionMessage: params.resolutionMessage, + cancellationMessage: params.cancellationMessage, serviceIds: params.updateServiceIds, environmentIds: params.updateEnvironmentIds, groupIds: params.updateGroupIds, + incidentTypeIds: params.updateIncidentTypeIds, + functionalityIds: params.updateFunctionalityIds, + labels: params.updateLabels, } case 'rootly_list_incidents': @@ -856,6 +944,7 @@ export const RootlyBlock: BlockConfig = { case 'rootly_list_severities': return { ...baseParams, + search: params.severitiesSearch, pageSize: params.severitiesPageSize ? Number(params.severitiesPageSize) : undefined, pageNumber: params.severitiesPageNumber ? Number(params.severitiesPageNumber) @@ -951,6 +1040,12 @@ export const RootlyBlock: BlockConfig = { updateServiceIds: { type: 'string', description: 'Updated service IDs' }, updateEnvironmentIds: { type: 'string', description: 'Updated environment IDs' }, updateGroupIds: { type: 'string', description: 'Updated team IDs' }, + updateKind: { type: 'string', description: 'Updated kind' }, + updatePrivate: { type: 'string', description: 'Whether incident is private' }, + updateIncidentTypeIds: { type: 'string', description: 'Updated incident type IDs' }, + updateFunctionalityIds: { type: 'string', description: 'Updated functionality IDs' }, + updateLabels: { type: 'string', description: 'Updated labels as JSON' }, + cancellationMessage: { type: 'string', description: 'Cancellation message' }, listIncidentsStatus: { type: 'string', description: 'Filter by status' }, listIncidentsSearch: { type: 'string', description: 'Search incidents' }, listIncidentsSeverity: { type: 'string', description: 'Filter by severity' }, @@ -983,6 +1078,7 @@ export const RootlyBlock: BlockConfig = { servicesSearch: { type: 'string', description: 'Search services' }, servicesPageSize: { type: 'string', description: 'Services page size' }, servicesPageNumber: { type: 'string', description: 'Services page number' }, + severitiesSearch: { type: 'string', description: 'Search severities' }, severitiesPageSize: { type: 'string', description: 'Severities page size' }, severitiesPageNumber: { type: 'string', description: 'Severities page number' }, teamsSearch: { type: 'string', description: 'Search teams' }, @@ -1024,6 +1120,7 @@ export const RootlyBlock: BlockConfig = { visibility: { type: 'string', description: 'Event visibility' }, occurredAt: { type: 'string', description: 'When the event occurred' }, createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, services: { type: 'json', description: 'List of services (id, name, slug, description, color)', diff --git a/apps/sim/tools/rootly/add_incident_event.ts b/apps/sim/tools/rootly/add_incident_event.ts index c1343ad1140..cc9f1fe0ede 100644 --- a/apps/sim/tools/rootly/add_incident_event.ts +++ b/apps/sim/tools/rootly/add_incident_event.ts @@ -67,6 +67,7 @@ export const rootlyAddIncidentEventTool: ToolConfig< visibility: null, occurredAt: null, createdAt: '', + updatedAt: '', }, error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, } @@ -82,6 +83,7 @@ export const rootlyAddIncidentEventTool: ToolConfig< visibility: attrs.visibility ?? null, occurredAt: attrs.occurred_at ?? null, createdAt: attrs.created_at ?? '', + updatedAt: attrs.updated_at ?? '', }, } }, @@ -107,5 +109,9 @@ export const rootlyAddIncidentEventTool: ToolConfig< type: 'string', description: 'Creation date', }, + updatedAt: { + type: 'string', + description: 'Last update date', + }, }, } diff --git a/apps/sim/tools/rootly/create_alert.ts b/apps/sim/tools/rootly/create_alert.ts index d289f356cd4..0687276f954 100644 --- a/apps/sim/tools/rootly/create_alert.ts +++ b/apps/sim/tools/rootly/create_alert.ts @@ -29,7 +29,7 @@ export const rootlyCreateAlertTool: ToolConfig { const attributes: Record = { summary: params.summary, + source: params.source, } if (params.description) attributes.description = params.description - if (params.source) attributes.source = params.source if (params.status) attributes.status = params.status if (params.externalId) attributes.external_id = params.externalId if (params.externalUrl) attributes.external_url = params.externalUrl diff --git a/apps/sim/tools/rootly/create_incident.ts b/apps/sim/tools/rootly/create_incident.ts index ae9d8ccc8fc..fa17b888e61 100644 --- a/apps/sim/tools/rootly/create_incident.ts +++ b/apps/sim/tools/rootly/create_incident.ts @@ -19,9 +19,9 @@ export const rootlyCreateIncidentTool: ToolConfig< }, title: { type: 'string', - required: true, + required: false, visibility: 'user-or-llm', - description: 'The title of the incident', + description: 'The title of the incident (auto-generated if not provided)', }, summary: { type: 'string', @@ -40,13 +40,14 @@ export const rootlyCreateIncidentTool: ToolConfig< required: false, visibility: 'user-or-llm', description: - 'Incident status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled)', + 'Incident status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed)', }, kind: { type: 'string', required: false, visibility: 'user-or-llm', - description: 'Incident kind (normal, test, example, backfilled, scheduled)', + description: + 'Incident kind (normal, normal_sub, test, test_sub, example, example_sub, backfilled, scheduled, scheduled_sub)', }, serviceIds: { type: 'string', @@ -100,9 +101,8 @@ export const rootlyCreateIncidentTool: ToolConfig< Authorization: `Bearer ${params.apiKey}`, }), body: (params) => { - const attributes: Record = { - title: params.title, - } + const attributes: Record = {} + if (params.title) attributes.title = params.title if (params.summary) attributes.summary = params.summary if (params.severityId) attributes.severity_id = params.severityId if (params.status) attributes.status = params.status diff --git a/apps/sim/tools/rootly/list_incident_types.ts b/apps/sim/tools/rootly/list_incident_types.ts index d2f7aa4cf80..71a906fd306 100644 --- a/apps/sim/tools/rootly/list_incident_types.ts +++ b/apps/sim/tools/rootly/list_incident_types.ts @@ -24,7 +24,7 @@ export const rootlyListIncidentTypesTool: ToolConfig< type: 'string', required: false, visibility: 'user-or-llm', - description: 'Search term to filter incident types', + description: 'Filter incident types by name', }, pageSize: { type: 'number', @@ -43,7 +43,7 @@ export const rootlyListIncidentTypesTool: ToolConfig< request: { url: (params) => { const queryParams = new URLSearchParams() - if (params.search) queryParams.set('filter[search]', params.search) + if (params.search) queryParams.set('filter[name]', params.search) if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) const qs = queryParams.toString() diff --git a/apps/sim/tools/rootly/list_incidents.ts b/apps/sim/tools/rootly/list_incidents.ts index af04caae772..1fecfd38e17 100644 --- a/apps/sim/tools/rootly/list_incidents.ts +++ b/apps/sim/tools/rootly/list_incidents.ts @@ -22,7 +22,7 @@ export const rootlyListIncidentsTool: ToolConfig< required: false, visibility: 'user-or-llm', description: - 'Filter by status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled)', + 'Filter by status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed)', }, severity: { type: 'string', diff --git a/apps/sim/tools/rootly/list_severities.ts b/apps/sim/tools/rootly/list_severities.ts index 72015d2a6dd..db5be4546e0 100644 --- a/apps/sim/tools/rootly/list_severities.ts +++ b/apps/sim/tools/rootly/list_severities.ts @@ -17,6 +17,12 @@ export const rootlyListSeveritiesTool: ToolConfig< visibility: 'user-only', description: 'Rootly API key', }, + search: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter severities', + }, pageSize: { type: 'number', required: false, @@ -34,6 +40,7 @@ export const rootlyListSeveritiesTool: ToolConfig< request: { url: (params) => { const queryParams = new URLSearchParams() + if (params.search) queryParams.set('filter[search]', params.search) if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) const qs = queryParams.toString() diff --git a/apps/sim/tools/rootly/types.ts b/apps/sim/tools/rootly/types.ts index 05db1eb788c..9fbb0d77353 100644 --- a/apps/sim/tools/rootly/types.ts +++ b/apps/sim/tools/rootly/types.ts @@ -7,7 +7,7 @@ export interface RootlyBaseParams { /** Create Incident */ export interface RootlyCreateIncidentParams extends RootlyBaseParams { - title: string + title?: string summary?: string severityId?: string status?: string @@ -66,11 +66,17 @@ export interface RootlyUpdateIncidentParams extends RootlyBaseParams { summary?: string severityId?: string status?: string + kind?: string + private?: boolean serviceIds?: string environmentIds?: string groupIds?: string + incidentTypeIds?: string + functionalityIds?: string + labels?: string mitigationMessage?: string resolutionMessage?: string + cancellationMessage?: string } export interface RootlyUpdateIncidentResponse extends ToolResponse { @@ -102,8 +108,8 @@ export interface RootlyListIncidentsResponse extends ToolResponse { /** Create Alert */ export interface RootlyCreateAlertParams extends RootlyBaseParams { summary: string + source: string description?: string - source?: string status?: string serviceIds?: string groupIds?: string @@ -164,6 +170,7 @@ export interface RootlyAddIncidentEventResponse extends ToolResponse { visibility: string | null occurredAt: string | null createdAt: string + updatedAt: string } } @@ -193,6 +200,7 @@ export interface RootlyListServicesResponse extends ToolResponse { /** List Severities */ export interface RootlyListSeveritiesParams extends RootlyBaseParams { + search?: string pageSize?: number pageNumber?: number } diff --git a/apps/sim/tools/rootly/update_incident.ts b/apps/sim/tools/rootly/update_incident.ts index a42e2566713..daf1396b1c0 100644 --- a/apps/sim/tools/rootly/update_incident.ts +++ b/apps/sim/tools/rootly/update_incident.ts @@ -46,7 +46,20 @@ export const rootlyUpdateIncidentTool: ToolConfig< required: false, visibility: 'user-or-llm', description: - 'Updated status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled)', + 'Updated status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed)', + }, + kind: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Incident kind (normal, normal_sub, test, test_sub, example, example_sub, backfilled, scheduled, scheduled_sub)', + }, + private: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Set incident as private (cannot be undone)', }, serviceIds: { type: 'string', @@ -66,6 +79,24 @@ export const rootlyUpdateIncidentTool: ToolConfig< visibility: 'user-or-llm', description: 'Comma-separated team/group IDs', }, + incidentTypeIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated incident type IDs to attach', + }, + functionalityIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated functionality IDs to attach', + }, + labels: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Labels as JSON object, e.g. {"platform":"osx","version":"1.29"}', + }, mitigationMessage: { type: 'string', required: false, @@ -78,6 +109,12 @@ export const rootlyUpdateIncidentTool: ToolConfig< visibility: 'user-or-llm', description: 'How was the incident resolved?', }, + cancellationMessage: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Why was the incident cancelled?', + }, }, request: { @@ -93,8 +130,28 @@ export const rootlyUpdateIncidentTool: ToolConfig< if (params.summary) attributes.summary = params.summary if (params.severityId) attributes.severity_id = params.severityId if (params.status) attributes.status = params.status + if (params.kind) attributes.kind = params.kind + if (params.private !== undefined) attributes.private = params.private if (params.mitigationMessage) attributes.mitigation_message = params.mitigationMessage if (params.resolutionMessage) attributes.resolution_message = params.resolutionMessage + if (params.cancellationMessage) attributes.cancellation_message = params.cancellationMessage + if (params.incidentTypeIds) { + attributes.incident_type_ids = params.incidentTypeIds + .split(',') + .map((s: string) => s.trim()) + } + if (params.functionalityIds) { + attributes.functionality_ids = params.functionalityIds + .split(',') + .map((s: string) => s.trim()) + } + if (params.labels) { + try { + attributes.labels = JSON.parse(params.labels) + } catch { + attributes.labels = params.labels + } + } if (params.serviceIds) { attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim()) } From e3ba4ab64b6f33bfd590ddbff1dfbbcf8cecc453 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Thu, 2 Apr 2026 11:28:45 -0700 Subject: [PATCH 7/8] fix(rootly): final validation fixes from OpenAPI spec audit - update_incident: change PATCH to PUT per OpenAPI spec - index.ts: add types re-export - types.ts: fix id fields to string | null (matches ?? null runtime) - block: add value initializers to 4 dropdowns missing them - registry: fix alphabetical order (incident_types before incidents) Co-Authored-By: Claude Opus 4.6 --- apps/sim/blocks/blocks/rootly.ts | 4 ++++ apps/sim/tools/rootly/index.ts | 1 + apps/sim/tools/rootly/types.ts | 18 +++++++++--------- apps/sim/tools/rootly/update_incident.ts | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/apps/sim/blocks/blocks/rootly.ts b/apps/sim/blocks/blocks/rootly.ts index 7920f114564..c00c6c836b5 100644 --- a/apps/sim/blocks/blocks/rootly.ts +++ b/apps/sim/blocks/blocks/rootly.ts @@ -221,6 +221,7 @@ export const RootlyBlock: BlockConfig = { { label: 'In Progress', id: 'in_progress' }, { label: 'Completed', id: 'completed' }, ], + value: () => '', condition: { field: 'operation', value: 'rootly_update_incident' }, }, { @@ -359,6 +360,7 @@ export const RootlyBlock: BlockConfig = { { label: 'In Progress', id: 'in_progress' }, { label: 'Completed', id: 'completed' }, ], + value: () => '', condition: { field: 'operation', value: 'rootly_list_incidents' }, }, { @@ -531,6 +533,7 @@ export const RootlyBlock: BlockConfig = { { label: 'Resolved', id: 'resolved' }, { label: 'Deferred', id: 'deferred' }, ], + value: () => '', condition: { field: 'operation', value: 'rootly_list_alerts' }, }, { @@ -773,6 +776,7 @@ export const RootlyBlock: BlockConfig = { { label: 'Draft', id: 'draft' }, { label: 'Published', id: 'published' }, ], + value: () => '', condition: { field: 'operation', value: 'rootly_list_retrospectives' }, }, { diff --git a/apps/sim/tools/rootly/index.ts b/apps/sim/tools/rootly/index.ts index b6a05b5d4cf..53b2c4beb60 100644 --- a/apps/sim/tools/rootly/index.ts +++ b/apps/sim/tools/rootly/index.ts @@ -11,4 +11,5 @@ export { rootlyListRetrospectivesTool } from '@/tools/rootly/list_retrospectives export { rootlyListServicesTool } from '@/tools/rootly/list_services' export { rootlyListSeveritiesTool } from '@/tools/rootly/list_severities' export { rootlyListTeamsTool } from '@/tools/rootly/list_teams' +export * from '@/tools/rootly/types' export { rootlyUpdateIncidentTool } from '@/tools/rootly/update_incident' diff --git a/apps/sim/tools/rootly/types.ts b/apps/sim/tools/rootly/types.ts index 9fbb0d77353..82dfd141491 100644 --- a/apps/sim/tools/rootly/types.ts +++ b/apps/sim/tools/rootly/types.ts @@ -22,7 +22,7 @@ export interface RootlyCreateIncidentParams extends RootlyBaseParams { } export interface RootlyIncidentData { - id: string + id: string | null sequentialId: number | null title: string slug: string | null @@ -120,7 +120,7 @@ export interface RootlyCreateAlertParams extends RootlyBaseParams { } export interface RootlyAlertData { - id: string + id: string | null summary: string description: string | null source: string | null @@ -182,7 +182,7 @@ export interface RootlyListServicesParams extends RootlyBaseParams { } export interface RootlyServiceData { - id: string + id: string | null name: string slug: string | null description: string | null @@ -206,7 +206,7 @@ export interface RootlyListSeveritiesParams extends RootlyBaseParams { } export interface RootlySeverityData { - id: string + id: string | null name: string slug: string | null description: string | null @@ -233,7 +233,7 @@ export interface RootlyListRetrospectivesParams extends RootlyBaseParams { } export interface RootlyRetrospectiveData { - id: string + id: string | null title: string status: string | null url: string | null @@ -259,7 +259,7 @@ export interface RootlyListTeamsParams extends RootlyBaseParams { } export interface RootlyTeamData { - id: string + id: string | null name: string slug: string | null description: string | null @@ -283,7 +283,7 @@ export interface RootlyListEnvironmentsParams extends RootlyBaseParams { } export interface RootlyEnvironmentData { - id: string + id: string | null name: string slug: string | null description: string | null @@ -307,7 +307,7 @@ export interface RootlyListIncidentTypesParams extends RootlyBaseParams { } export interface RootlyIncidentTypeData { - id: string + id: string | null name: string slug: string | null description: string | null @@ -331,7 +331,7 @@ export interface RootlyListFunctionalitiesParams extends RootlyBaseParams { } export interface RootlyFunctionalityData { - id: string + id: string | null name: string slug: string | null description: string | null diff --git a/apps/sim/tools/rootly/update_incident.ts b/apps/sim/tools/rootly/update_incident.ts index daf1396b1c0..bebaf6f84e0 100644 --- a/apps/sim/tools/rootly/update_incident.ts +++ b/apps/sim/tools/rootly/update_incident.ts @@ -119,7 +119,7 @@ export const rootlyUpdateIncidentTool: ToolConfig< request: { url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}`, - method: 'PATCH', + method: 'PUT', headers: (params) => ({ 'Content-Type': 'application/vnd.api+json', Authorization: `Bearer ${params.apiKey}`, From 9f6b8eaede27df6fb05a5bd985521acf4f1e71d7 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Thu, 2 Apr 2026 11:29:30 -0700 Subject: [PATCH 8/8] reorg --- apps/sim/tools/registry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 9e6c2467ade..eba06d416e8 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -3797,8 +3797,8 @@ export const tools: Record = { rootly_list_alerts: rootlyListAlertsTool, rootly_list_environments: rootlyListEnvironmentsTool, rootly_list_functionalities: rootlyListFunctionalitiesTool, - rootly_list_incidents: rootlyListIncidentsTool, rootly_list_incident_types: rootlyListIncidentTypesTool, + rootly_list_incidents: rootlyListIncidentsTool, rootly_list_retrospectives: rootlyListRetrospectivesTool, rootly_list_services: rootlyListServicesTool, rootly_list_severities: rootlyListSeveritiesTool,