# Create or Update Detection Rule — Elastic Security

> Create a new Elastic Security detection rule via POST /api/detection_engine/rules, or full-replace update an existing one when id is provided, via PUT /api/detection_engine/rules. On update, the tool first fetches the rule's current…

- Key: `elastic_security-create-or-update-detection-rule`
- Type: Action (Write)
- Version: 0.0.1
- App: Elastic Security (`elastic_security`) — https://pipedream.com/apps/elastic-security.md
- This page (HTML): https://pipedream.com/apps/elastic-security/actions/create-or-update-detection-rule
- Hints: open-world
- Source: https://github.com/PipedreamHQ/pipedream/blob/master/components/elastic_security/actions/create-or-update-detection-rule/create-or-update-detection-rule.mjs

## Description

Create a new Elastic Security detection rule via POST /api/detection_engine/rules, or full-replace update an existing one when `id` is provided, via PUT /api/detection_engine/rules. On update, the tool first fetches the rule's current definition and merges your supplied fields into it, so you only need to pass the fields you want to change — Kibana's underlying PUT still requires the full definition, but this tool handles that for you. Run **Find Detection Rules** first to obtain the `id` for updates (it also accepts `ruleId` if that's all you have). `name`, `description`, `riskScore`, `severity`, and `type` are required when creating (no `id`); optionally set `ruleId` on create to assign a custom `rule_id` instead of letting Kibana generate one. For `type: threshold` rules, set `threshold`. For `type: threat_match` rules, set `threatIndex` and `threatMapping`. Use `additionalFields` as an escape hatch for any other type-specific fields (e.g. `anomaly_threshold` for `machine_learning` rules). Example: calling with `name: "Suspicious PowerShell"`, `description: "..."`, `riskScore: 60`, `severity: "high"`, `type: "query"`, `query: "process.name: powershell.exe"` returns `{ id: "7ac3...", rule_id: "f3bb...", name: "Suspicious PowerShell", enabled: true, ... }`; calling again with that `id` and `riskScore: 80` returns the same rule with only the risk score changed. [See the create documentation](https://www.elastic.co/docs/api/doc/kibana/operation/operation-createrule) and the [update documentation](https://www.elastic.co/docs/api/doc/kibana/operation/operation-updaterule)

## Props

| Prop | Type | Required | Description |
|---|---|---|---|
| `id` | `string` | No | The Kibana internal UUID of an existing rule to update (e.g. 7ac3c66d-f0b4-4f7c-a576-7bb91bf4e9ce). This is the sole trigger for update mode — omit it to create a new rule. Run Find Detection Rules first to obtain valid IDs (it accepts either id or ruleId for lookup). |
| `ruleId` | `string` | No | When creating (no id): an optional custom rule_id to assign to the new rule, e.g. my-custom-rule-id — if omitted, Kibana generates one. Not used to identify a rule for update; use id for that (run Find Detection Rules with ruleId first if that's all you have, to get its id). |
| `name` | `string` | No | Human-readable rule name (e.g. Suspicious PowerShell Execution). Required when creating. |
| `description` | `string` | No | Description of what the rule detects. Required when creating. |
| `riskScore` | `integer` | No | Risk score from 0 to 100. Required when creating. |
| `severity` | `string` | No | Rule severity. One of: low, medium, high, critical. Required when creating. |
| `type` | `string` | No | Rule type discriminator. One of: query, eql, saved_query, threshold, threat_match, machine_learning, new_terms, esql. Required when creating. Cannot be changed on update. |
| `query` | `string` | No | Detection query in KQL or Lucene (required for query/saved_query/eql style rules), e.g. process.name: powershell.exe. |
| `language` | `string` | No | Query language: kuery or lucene. |
| `index` | `string[]` | No | Index patterns the rule runs against (e.g. logs-*, winlogbeat-*). |
| `enabled` | `boolean` | No | Whether the rule is enabled. Defaults to true on create. |
| `tags` | `string[]` | No | Tags to apply to the rule. Run List Tags first to reuse existing tags instead of creating near-duplicates. On update, this replaces the rule's existing tag set entirely. |
| `interval` | `string` | No | How often the rule runs, as date-math (e.g. 5m). Defaults to 5m on create. |
| `from` | `string` | No | Start of the rule's lookback window as date-math (e.g. now-6m). Defaults to now-6m on create. |
| `maxSignals` | `integer` | No | Maximum number of alerts the rule can create per run. Minimum 1, maximum 1000. Defaults to 100 on create. |
| `threshold` | `object` | No | Threshold configuration, required for type: threshold rules. Example: {"field":["host.name"],"value":5} fires when 5+ matching events share the same host.name. |
| `threatIndex` | `string[]` | No | Index patterns containing threat intelligence indicators, required for type: threat_match rules. Example: ["logs-ti_*"]. |
| `threatMapping` | `object` | No | A single threat-match group, required for type: threat_match rules. Shape: {"entries":[{"field":"source.ip","type":"mapping","value":"threat.indicator.ip"}]}, matching a local event field against a threat indicator field. For multiple match groups, use additionalFields.threat_mapping (an array of these objects) instead. |
| `additionalFields` | `object` | No | Additional rule fields to merge into the request body, for type-specific configuration not covered by other parameters (e.g. {"anomaly_threshold":50,"machine_learning_job_id":["job-1"]} for machine_learning rules, or threat_mapping as an array for multi-group threat_match rules, since threatMapping only supports one group). id, rule_id, and type here are always ignored — use the dedicated type parameter instead. Read-only fields (created_at, updated_at, revision, etc.) are also always ignored, even though they have no dedicated parameter of their own. Any other key here is ignored if you've also set its dedicated parameter (that value wins); otherwise it's used as given. |

## Run it

**MCP**

```ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
import { PipedreamClient } from "@pipedream/sdk"

const pd = new PipedreamClient({
  projectId: process.env.PIPEDREAM_PROJECT_ID!,
  clientId: process.env.PIPEDREAM_CLIENT_ID!,
  clientSecret: process.env.PIPEDREAM_CLIENT_SECRET!,
  projectEnvironment: "production",
})

const accessToken = await pd.rawAccessToken

const transport = new StreamableHTTPClientTransport(
  new URL("https://remote.mcp.pipedream.net/v3"),
  {
    requestInit: {
      headers: {
        Authorization: `Bearer ${accessToken}`,
        "x-pd-project-id": process.env.PIPEDREAM_PROJECT_ID!,
        "x-pd-environment": "production",
        "x-pd-external-user-id": "{external_user_id}", // any stable ID for this user in your system
        "x-pd-app-slug": "elastic_security",
      },
    },
  },
)

const mcp = new Client({ name: "my-agent", version: "1.0.0" })
await mcp.connect(transport)

const { tools } = await mcp.listTools()

// listTools() hands your model this tool's input schema, so it can
// fill the arguments itself:
const result = await mcp.callTool({
  name: "elastic_security-create-or-update-detection-rule",
  arguments: {
    id: "Rule ID",
    ruleId: "Rule ID (User-defined)",
  },
})
```

**TypeScript**

```ts
import { PipedreamClient } from "@pipedream/sdk"

const pd = new PipedreamClient({
  projectId: process.env.PIPEDREAM_PROJECT_ID!,
  clientId: process.env.PIPEDREAM_CLIENT_ID!,
  clientSecret: process.env.PIPEDREAM_CLIENT_SECRET!,
  projectEnvironment: "production",
})

const result = await pd.actions.run({
  id: "elastic_security-create-or-update-detection-rule",
  externalUserId: "{external_user_id}", // any stable ID for this user in your system
  configuredProps: {
    elastic_security: { authProvisionId: "apn_xxxxxxx" },
    id: "Rule ID",
    ruleId: "Rule ID (User-defined)",
  },
})

console.log(result)
```

**cURL**

```bash
curl -X POST https://api.pipedream.com/v1/connect/{project_id}/actions/run \
  -H "Content-Type: application/json" \
  -H "X-PD-Environment: production" \
  -H "Authorization: Bearer {access_token}" \
  -d '{
    "external_user_id": "{external_user_id}",
    "id": "elastic_security-create-or-update-detection-rule",
    "configured_props": {
      "elastic_security": { "authProvisionId": "apn_xxxxxxx" },
      "id": "Rule ID",
      "ruleId": "Rule ID (User-defined)"
    }
  }'
```

---

- App: https://pipedream.com/apps/elastic-security.md · All apps: https://pipedream.com/apps
