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

# Send Alert Event

> Trigger, acknowledge or resolve an alert. Repeats fold into one alert instead of paging twice.

Use this endpoint for operational faults: a job that gave up, a queue that stopped
draining, a webhook that keeps failing. Repeated events for the same problem fold
into one open alert and only the first one notifies.

Use [Create Incident](/api-reference/incidents/create) instead when you want
something customer-facing on a status page. An alert is internal until somebody
promotes it.

<Note>
  Requires an API key with the `alerts:write` scope.
</Note>

## Body Parameters

<ParamField body="event_action" type="string" default="trigger">
  `trigger`, `acknowledge` or `resolve`
</ParamField>

<ParamField body="dedup_key" type="string">
  What "the same problem" means to you, for example
  `writeback:ezyvet:merchant-a576`. Send the same key to fold repeats into one
  alert, and to acknowledge or resolve it later.

  Omit it and Kōdo groups on the summary with numbers and UUIDs stripped, so
  "job 41 failed" and "job 42 failed" land on one alert.
</ParamField>

<ParamField body="summary" type="string" required>
  What went wrong. Required when `event_action` is `trigger`, max 200 characters.
</ParamField>

<ParamField body="severity" type="string" default="warning">
  `info`, `warning`, `error` or `critical`. While an alert is open it keeps the
  worst severity seen.
</ParamField>

<ParamField body="source" type="string">
  Where the event came from, for example `writeback-worker`.
</ParamField>

<ParamField body="component" type="string">
  What is affected, for example `ezyvet`.
</ParamField>

<ParamField body="service" type="string">
  Service name or UUID to attach the alert to.
</ParamField>

<ParamField body="details" type="object">
  Free-form context. Shown on the alert and in the notification body.
</ParamField>

## Response

<ResponseField name="alert_id" type="string">
  The alert this event landed on, or `null` if nothing matched.
</ResponseField>

<ResponseField name="outcome" type="string">
  `triggered` (new alert, team notified), `deduped` (folded into an open alert,
  nobody notified), `acknowledged`, `resolved`, or `ignored` (an acknowledge or
  resolve with nothing open).
</ResponseField>

<ResponseField name="fingerprint" type="string">
  The grouping key Kōdo used.
</ResponseField>

<ResponseField name="occurrence_count" type="integer">
  How many events have folded into this alert.
</ResponseField>

## Who gets told

A `triggered` outcome delivers to every notification channel with
`notify_on_alert` enabled, and emails the team as a floor, capped at 10 alert
emails per hour per organization. A `resolved` outcome goes to the channels only.

`notify_on_alert` is on by default for every channel type except **voice and
SMS**, which interrupt a person and cost money per delivery, so they opt in from
the channel's settings.

A `deduped` outcome notifies nobody. That is the point: a fault that repeats
every five minutes costs you one notification, not 288 a day.

<RequestExample>
  ```bash Trigger theme={null}
  curl -X POST "https://kodostatus.com/api/v1/events" \
    -H "X-API-Key: your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "event_action": "trigger",
      "dedup_key": "writeback:ezyvet:merchant-a576",
      "summary": "ezyVet writeback failing",
      "severity": "error",
      "source": "writeback-worker",
      "component": "ezyvet",
      "details": { "invoice_id": "579784", "attempts": 3 }
    }'
  ```

  ```bash Resolve theme={null}
  curl -X POST "https://kodostatus.com/api/v1/events" \
    -H "X-API-Key: your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "event_action": "resolve",
      "dedup_key": "writeback:ezyvet:merchant-a576"
    }'
  ```

  ```javascript Node.js theme={null}
  async function alert(body) {
    await fetch("https://kodostatus.com/api/v1/events", {
      method: "POST",
      headers: {
        "X-API-Key": process.env.KODO_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    });
  }

  try {
    await drainWritebacks();
    await alert({ event_action: "resolve", dedup_key: "writeback:ezyvet" });
  } catch (error) {
    await alert({
      event_action: "trigger",
      dedup_key: "writeback:ezyvet",
      summary: "ezyVet writeback failing",
      severity: "error",
      details: { message: error.message },
    });
  }
  ```
</RequestExample>

<ResponseExample>
  ```json Triggered theme={null}
  {
    "alert_id": "8f1c2f5e-4a1b-4a3c-9c2f-0d5a1e7b4c33",
    "outcome": "triggered",
    "fingerprint": "3b9c1f0a7d2e4c58",
    "occurrence_count": 1
  }
  ```

  ```json Deduped theme={null}
  {
    "alert_id": "8f1c2f5e-4a1b-4a3c-9c2f-0d5a1e7b4c33",
    "outcome": "deduped",
    "fingerprint": "3b9c1f0a7d2e4c58",
    "occurrence_count": 7
  }
  ```
</ResponseExample>
