---
title: "Get alert templates for a check"
method: GET
path: "/api/v1/checks/{id}/templates"
tags: ["Checks"]
---

# Get alert templates for a check

`GET /api/v1/checks/{id}/templates`

## Request

- Base URL: `https://app.lastping.dev`
- URL: `https://app.lastping.dev/api/v1/checks/{id}/templates`
- Auth: API key in header `X-Api-Key`

Returns the custom alert message templates configured for the check.
Returns an empty `templates` map when no custom templates are set — the
built-in plain-language defaults are used.

**Key format:** keys are either a bare event type (`down`, `recovery`,
`fail`, `every-run`, `success`, `started`, `blocked`, `note`) or an
`event_type/cause` pair for a per-cause override (e.g. `down/silence`,
`down/overrun`, `down/never_started`, `fail/runaway`).  Per-cause
entries take priority over the event-type-wide entry at notification
time. Like `every-run`, `success` and `started`, the `blocked` and
`note` events are always recorded with an empty cause — they are not
incident-backed — so only the bare `blocked`/`note` keys are ever
matched; a `blocked/`-prefixed per-cause key is accepted but never
selected. (The `blocked`-CAUSE `down` incident that opens when a
block outlasts `blocked_timeout_s` is a separate event, templated via
the `down` or `down/blocked` keys.)

Template bodies may reference allowlisted `{variable}` placeholders:
`{check_name}`, `{event}`, `{status}`, `{cause}`, `{last_ping}`,
`{schedule}`, `{incident_url}`, `{run_url}`, `{branch}`, `{commit}`,
`{actor}`, `{failing_stage}`, `{duration}`, `{latency}`, `{status_code}`,
`{url}`, `{last_step}`, `{step_count}`, `{run_duration}`, `{body}`,
`{detail}`, `{title}`.
Unknown placeholders are **rejected** at write time. `{body}` is the
triggering ping's free-text body (`body_excerpt`, see the `Ping`
schema), truncated to 500 characters with a trailing "…" if longer
(every channel's message-size limit is smaller than the 10000-byte
stored excerpt, so the cap is applied once, centrally, rather than
per channel). It is populated for every event type that is emitted
FROM an incoming ping — `note`, `blocked`, `every-run`, `success`,
`started`, `fail` (the failing ping's body for cause `fail`, or
whichever ping tripped the rate ceiling for cause `runaway`), and
`recovery` (the closing success ping's body) — from that ping's own
`body_excerpt`, and renders empty when that ping had none. It always
renders empty for `down`: that event comes from a missed
deadline (`silence`, `overrun`, `stalled`,
`never_started`, or a `blocked_timeout_s` expiry), which has no
incoming ping to read a body from. It also always renders empty for
the destinations page's
synthetic "send test alert" action, which has no ping at all.

`{detail}` is the CI failure excerpt from the incident's `detail`
field (see `POST /ci/{id}/detail` and the `Incident` schema), truncated
with the same 500-character cap as `{body}`. It is DISTINCT from
`{body}`: `{body}` is what the agent's own ping said; `{detail}` is
what the CI job separately posted. It is populated when the
incident is CI-bound and a detail was posted, and on an `upstream`
incident (`fail/upstream`), where it names the last API error type
(`Last error: server_error`). It renders empty otherwise, including
when the detail POST arrives after the alert has already been sent,
since the two requests race independently.

`{last_step}`, `{step_count}` and `{run_duration}` describe the run that
was in flight when the incident opened, and are populated on `stalled`
and `overrun` incidents only — elsewhere they render empty rather than
report a number about a different run.  `{run_duration}` is the absolute
elapsed time from the run's `/start`; a run that wedged before its first
`/step` leaves `{last_step}` and `{step_count}` empty rather than `0`.

`{title}` is the title of the run the alert is about: whichever text
set the run's title FIRST for that run's `rid` — the free-text body
posted with the run's `/start` ping, or a later
`POST .../title?rid=<run-id>` sent any time while the run was still
open (see the `CIRun` schema's `title` field). Populated for a `fail`
when the failing ping carried a `rid` whose run had a title set by
either path, and for `stalled`/`overrun` under the same
run-identification rule as `{last_step}` above. Renders empty for the
common case — a run with no title, which is every run for a monitor
that has not started setting one — and for every event type not
about one specific run (`down` with cause
`silence`/`never_started`/`runaway`, `recovery`, `every-run`,
`success`, `started`, `blocked`, `note`).

Not every placeholder is populated for every provider. `{failing_stage}`
in particular renders empty on Jenkins (its Notification Plugin payload
has no step detail) and on GitHub unless the repository webhook also
subscribes to **Workflow jobs** — see the `failing_stage` field on the
`CIMeta` schema.

## Response `200`

Current custom templates for this check (may be an empty map).

- AlertTemplatesResponse: Custom alert message templates for a check, keyed by event type or event-type/cause.
  - `templates` object, required: Map of template key → custom template body. **Key format:** - Event-type-wide: `"down"`, `"fail"`, `"recovery"`, `"every-run"`, `"success"`, `"started"`, `"blocked"`, `"note"`. - Per-cause override: `"event_type/cause"`, e.g. `"down/silence"`, `"down/overrun"`, `"down/never_started"`, `"fail/runaway"`. Per-cause keys take priority over the event-type-wide key. Only keys with a custom template are included; absent keys fall back to the per-cause built-in default, then to the event-type-wide custom template, then to the built-in default text.

## Other responses

- `401`: Missing or invalid API key.
- `404`: Check not found in this project.
- `429`: Too many requests. The per-principal fixed-window rate limit has been exceeded. Retry after the number of seconds indicated by the `Retry-After` response header. The problem body's `code` is `RATE_LIMITED`. It names this limiter specifically, so a client can tell it apart from the application-level throttles that also answer 429 with their own codes and their own, much longer, `Retry-After` values (`TEST_PING_RATE_LIMITED`, `TOO_MANY_ATTEMPTS`). Back off globally on `RATE_LIMITED`; back off on the one operation for the others. The principal is the API key for a key-authenticated request and the USER for a browser session, so every tab one person has open shares one budget.

---

[API](https://skmtc.dev/lastping/apis/lastping-management-api.md) · [All operations](https://skmtc.dev/lastping/apis/lastping-management-api/llms.txt) · [OpenAPI document](https://skmtc.dev/lastping/apis/lastping-management-api/revisions/bbc6eda91c46?raw)
