> Section: [5. Notifications](https://jaque.sh/docs/notifications/contacts-and-policies.md)
> Next: notifications/escalations-and-windows
> Index: https://jaque.sh/llms.txt


Deciding that an object is broken and telling someone are two different
problems, and jaque keeps them in two different places. The state machine
decides; a notification policy names who is told and under what rule. A
policy is a named entry under `notifications:`, built from contacts, a
set of time windows and a ladder of escalation levels, and it is attached
to a host or service by name. An object with no policy never notifies,
and that is the default. The ladder's `threshold` and `renotify_interval`
are evaluated by the `notify` package on every notifiable transition; see
[Escalations and windows](https://jaque.sh/docs/notifications/escalations-and-windows.md)
for the exact mechanics.

## 1. Contact types

A contact is one of seven discriminated variants of `#Contact`, told
apart by `type`. Credentials live outside the contact, with one
exception: a `slack` contact's `webhook_url` is both address and
credential.

```cue
contacts: {
	alice:       {type: "email", address: "alice@example.com"}
	oncall_hook: {type: "webhook", url: "https://hooks.example.com/oncall"}
	bob:         {type: "command", command: "page", args: ["-q"], address: "bob@example.com"}
	dave:        {type: "telegram", chat_id: "555444333"}
	pd:          {type: "pagerduty", routing_key: "R0UT1NGK3Y"}
	chat:        {type: "slack", webhook_url: "https://hooks.slack.com/services/T0/B0/XXXX"}
	inc:         {type: "incidentio", alert_source_config_id: "01ABCDEF"}
}
```

| Type | Required field | Delivered by |
|---|---|---|
| `email` | `address` | SMTP, or the system `mail` binary when no relay is configured |
| `webhook` | `url` | an HTTP POST |
| `command` | `command` | a top-level command template, run as a process |
| `telegram` | `chat_id` | the Telegram Bot API |
| `pagerduty` | `routing_key` | the PagerDuty Events API v2 |
| `slack` | `webhook_url` | an HTTP POST to the webhook |
| `incidentio` | `alert_source_config_id` | the incident.io alert events HTTP source |

An `email` contact also takes `subject_template` and `body_template`; a
`telegram` contact also takes `template`. Each defaults to `""`, meaning
"use the default template". See [Adapters](https://jaque.sh/docs/notifications/adapters.md)
for the fields available to a template and the default text.

`email`, `webhook`, `telegram` and `slack` contacts also take
`group_wait`, a duration string defaulting to `"0s"`, meaning no
grouping. A non-zero `group_wait` buffers this contact's notifications
into one digest per window instead of sending each as it arrives:

```cue
alice: {type: "email", address: "alice@example.com", group_wait: "30s"}
```

`command`, `pagerduty` and `incidentio` contacts do not take
`group_wait`. See [Adapters](https://jaque.sh/docs/notifications/adapters.md) for why, and
for the digest formats.

A `command` contact names a key in the top-level `commands` map, the same
indirection a [command check](https://jaque.sh/docs/checks/command-checks.md) uses. `args`
is passed through; `address` is what `$CONTACTEMAIL$` expands to for that
contact and defaults to the empty string. `webhook` and `command` are how
anything without a native adapter is reached: a pager service, a chat
incoming webhook, a script that speaks whatever the receiver speaks. If
it accepts HTTP or command-line arguments, it is a contact.

A `telegram` contact carries only `chat_id`. The bot token is a
process-level secret, configured once per jaque instance; see
[Adapters](https://jaque.sh/docs/notifications/adapters.md).

Any contact may also carry `notification_period`, the name of a
timeperiod outside which it is not reachable; see
[Timeperiods](https://jaque.sh/docs/config/timeperiods.md).

## 2. A policy

```cue
timeperiods: business_hours: {
	ranges: {
		monday:  "09:00-17:00"
		tuesday: "09:00-17:00"
	}
}

notifications: business_hours: {
	period: "business_hours"
	levels: [
		{contacts: ["alice"], threshold: 3, renotify_interval: "30m"},
		{contacts: ["oncall_hook", "bob", "dave"], renotify_interval: "10m"},
	]
}
```

`period` names a timeperiod from the top-level `timeperiods` map; empty,
the default, means the policy is always open. See
[Timeperiods](https://jaque.sh/docs/config/timeperiods.md) for the range syntax. `levels` is
a non-empty list of escalation levels evaluated in order, each naming the
contacts it reaches. A policy with no levels is a config error rather
than a silent policy. The semantics of `threshold`, `renotify_interval`
and the periods are specified in
[Escalations and windows](https://jaque.sh/docs/notifications/escalations-and-windows.md).

## 3. Wiring a policy to an object

A host or service refers to a policy by name through its `notification`
field.

```cue
hosts: gw: {
	address: "192.168.1.1"
	check: {type: "icmp", host: "192.168.1.1"}
	notification: "business_hours"
	services: ssh: {
		check:        {type: "tcp", address: "192.168.1.1:22"}
		notification: "business_hours"
	}
}
```

`notification` defaults to `""`, and an empty name means the object never
notifies. That is exactly the behaviour every configuration written before
contacts and policies existed already had, so adding notifications is
opt-in per object and never a silent change to an existing config. A
service does not inherit its host's policy; each object names its own.

## 4. What a policy is not

A policy does not decide that something is broken. Problem-class
notifications come only from hard transitions of the
[state model](https://jaque.sh/docs/concepts/state-model.md); a single failed check never
reaches a policy. A policy also does not deliver: it produces a decision
that the engine appends to the event log, and delivery is a separate
concern with its own durability rules, described in
[Adapters](https://jaque.sh/docs/notifications/adapters.md).

## 5. Switching notifications off at runtime

`DISABLE_NOTIFICATIONS` and `ENABLE_NOTIFICATIONS` switch every
notification off or on for the whole program; the `SetProgramNotifications`
call does the same.

`DISABLE_HOST_NOTIFICATIONS`/`ENABLE_HOST_NOTIFICATIONS` and
`DISABLE_SVC_NOTIFICATIONS`/`ENABLE_SVC_NOTIFICATIONS` switch one object
off or on; the `SetObjectNotifications` call does the same, for one
object or every object a label selector matches.

`DISABLE_CONTACT_HOST_NOTIFICATIONS`/`ENABLE_CONTACT_HOST_NOTIFICATIONS`
and `DISABLE_CONTACT_SVC_NOTIFICATIONS`/`ENABLE_CONTACT_SVC_NOTIFICATIONS`
switch one contact off or on for one object kind; the
`SetContactNotifications` call does the same. Every one of these toggles
is a fact on the event log, so it survives a restart.

## 6. Security considerations

A contact is a destination. SMTP credentials, the Telegram bot token and
the incident.io token are process-level flags or environment variables and
are not expressible in `contacts:`, so a config file can be shared without
leaking those. Two contact types carry a value that is itself a secret:
the `slack` webhook URL and the `pagerduty` routing key. A config that
declares either has to be treated as a secret; see
[Adapters](https://jaque.sh/docs/notifications/adapters.md). A `command` contact runs a process on the host that
delivers; the `commands` map is part of the trusted configuration, and
`args` are passed to it verbatim.
