-
Notifications
You must be signed in to change notification settings - Fork 0
Notifiers
Notifiers receive every event that passes their when expression — they are not tied to the scm.provider annotation. All support name, enabled, when; message-based ones support template. For most (slack, teams, discord) the template is optional — omit it for the built-in structured message. For email and grafana a template is required (the chart supplies a shipped default when you omit the field). See Templates → Supplying a template.
notifiers:
slack:
- name: prod-alerts
enabled: true
secretRef:
name: slack-webhook
channel: "#prod-alerts"
when: 'event.Namespace == "production" && stateIn("failure", "error")'
template: |
:rotating_light: *{{.PipelineName}}* failed in *{{.Namespace}}*
Run: {{.RunName}} · Commit: `{{ .CommitSHA | trunc 8 }}`
{{if .TargetURL}}<{{.TargetURL}}|View logs>{{end}}Tip: without a
when, a notifier fires for every state of every run — includingrunning. Production configs almost always wantstateIn("failure", "error")or terminal-states-only.
-
Auth: incoming webhook (
webhook_url.secretRef.name→ keywebhook_url) or bot token (bot_token.token.secretRef.name→ keytoken, plusbot_token.channel_id). - Extras:
channel,username,icon_emoji. Message uses Slack mrkdwn. -
mode: upsertedits one message per run (requires bot token);thread_tsposts/updates the message as a reply in that thread.
-
Auth: incoming webhook URL (
webhook_url.secretRef.name→ keywebhook_url). - Rendered as an Adaptive Card; keep templates concise.
-
Auth: webhook URL (
webhook_url.secretRef.name) or bot token (bot_token.*with channel snowflake ID). Extra:username. -
mode: upsertedits one message per run instead of posting a new one.
-
Auth: Events API v2 integration key (
integration_key.secretRef.name→ keyintegration_key). - Extra:
severity. No template — the relay builds the alert payload. Pair withwhen: 'stateIn("failure", "error")'(and resolve flows on success). -
acknowledge_on_running: true(opt-in) sends an acknowledge event while the run is in progress.
-
Auth: API key (
api_key.secretRef.name→ keyapi_key). Extras:site(e.g.datadoghq.eu),tags. - Emits Datadog events for correlation on dashboards/monitors.
Posts a deployment/event marker to the Grafana Annotations API — the vertical line that lets you correlate "the graph changed here" with "we deployed here".
grafana:
- name: deploy-markers
enabled: true
url: https://grafana.company.example.com
token:
secretRef:
name: grafana-token # service-account token, key "token"
tags: ["deploy"] # added to: tekton-events-relay, <state>
# dashboard_uid: abc123 # optional: scope to a dashboard (org-wide when empty)
# panel_id: 4 # optional: scope to a panel (requires dashboard_uid)
when: 'isPipelineRun() && event.PipelineName.startsWith("deploy-") && stateIn("success", "failure")'The annotation timestamp is the run's finish time. The marker template is required: in the Helm chart it is supplied inline, via configmapRef, or omitted to use the shipped default (deploy-marker.tmpl, {{.PipelineName}} {{.State}} ({{.RunName}})). See Templates.
-
Auth: a Grafana service-account token (
token.secretRef, keytoken), sent asAuthorization: Bearer. Grafana's API does not accept OAuth2 client-credentials tokens, so there is nooauth2option here. The token file is re-read on every request, so rotating the Secret applies without a pod restart. -
Scope (optional):
dashboard_uidscopes the annotation to one dashboard (organization-wide when empty);panel_idfurther scopes it to one panel and requiresdashboard_uid.
Sends pipeline events as email over SMTP (STARTTLS on 587 by default, implicit TLS on 465, or unencrypted in-cluster relays on 25).
email:
- name: oncall
enabled: true
host: smtp.example.com
from: ci@example.com
to: ["oncall@example.com"]
# cc: ["leads@example.com"] # optional visible carbon-copy
# bcc: ["audit@example.com"] # optional blind carbon-copy (envelope only)
# reply_to: noreply@example.com # optional Reply-To override
# subject and template are required; supply inline, via configmapRef,
# or omit to use the shipped defaults (email-subject.tmpl / email-default.tmpl):
# subject:
# value: "[tekton] {{ .PipelineName }} — {{ .State }}"
# template:
# configmapRef:
# name: my-email-templates
# key: body.tmpl
html: false
when: 'isPipelineRun() && stateIn("failure", "error")'-
Auth: optional
username+ password (secretRef, keypassword). For XOAUTH2 (Gmail / Microsoft 365) setauth.xoauth2: trueand supply the access token viaauth.token_file(re-read per send); it is mutually exclusive with the password. Omit auth entirely for unauthenticated relays. -
Recipients:
tois required;ccandbccare optional lists (bccis envelope-only, never written to headers).reply_tooverrides the Reply-To header (defaults tofrom). -
subjectandtemplate(body) are both required Go templates, resolvable the same way as every other template — inline string,configmapRef, or omitted→shipped default. See Templates. -
html: truesendstext/html; subject CR/LF is stripped to prevent header injection.
Creates a Sentry release (version = CommitSHA, Sentry's recommended scheme) and marks a deploy to the environment (scm.context annotation, default production) — unlocking "this error first appeared in commit X". Fires only on success; creating an existing release is an upsert.
sentry:
- name: sentry
enabled: true
org: acme # organization slug
projects: ["api"]
token:
secretRef:
name: sentry-token # auth token, key "token"
# base_url: https://sentry.company.example.com # self-hosted
when: 'isPipelineRun() && event.PipelineName.startsWith("deploy-")'-
Auth: a Sentry auth token (internal-integration / org / personal —
token.secretRef, keytoken), sent asAuthorization: Bearer. Sentry's API does not accept OAuth2 client-credentials tokens, so there is nooauth2option here. The token file is re-read per request, so Secret rotation applies without a pod restart.
Sends the event as JSON to any HTTP endpoint — the escape hatch for systems without a dedicated notifier.
webhook:
- name: devlake
enabled: true
url:
secretRef:
name: devlake-webhook # key "url"
headers:
X-Source: tekton-events-relay
transform: |
{pipeline: .pipeline_name, result: .state, sha: .commit_sha,
startedDate: .started_at, finishedDate: .finished_at}
when: 'isPipelineRun() && stateIn("success", "failure")'-
Auth (
auth.type):bearer(auth.token.secretRef),basic(auth.username.secretRef+auth.password.secretRef),apikey(auth.token.secretRef+ customheader),hmac(auth.hmac_secret.secretRef), oroauth2(OAuth2 client credentials —auth.oauth2.{client_id,client_secret,token_url}).-
OAuth2 (
type: oauth2) fetches the access token fromtoken_urland sends it asAuthorization: Bearer, auto-refreshed before expiry.grant_typedefaults toclient_credentials; forrefresh_token, supply a pre-obtainedrefresh_token(the relay exposes no redirect, so do the interactive consent out of band). Forbearer/basic/apikey/hmac, the underlying secret files are re-read on every request, so rotating the Secret applies without a pod restart.
-
OAuth2 (
-
transform: a gojq expression reshaping the event JSON into whatever schema the destination expects — see the DevLake example.
Jira is a top-level jira: integration (sibling of scm: / notifiers:), not a chat notifier — it acts on the work item linked to the run. Each instance has actions of type comment or transition.
jira:
- name: default
enabled: true
base_url: https://yourorg.atlassian.net # Cloud, or your Data Center URL
# api_version: "3" # "2" (default, plain text) or "3" (Atlassian Document Format)
auth:
email: ci@example.com # set ⇒ Cloud basic auth; omit ⇒ Data Center bearer (PAT)
token:
secretRef:
name: jira-token # API token / PAT, key "token"
actions:
- name: result-comment
type: comment
enabled: true
when: 'isPipelineRun() && stateIn("success", "failure", "error")'
# template: # omit for the shipped default (jira-comment.tmpl)
# value: | # …inline, or
# Pipeline {{ .State }}: {{ .PipelineName }}
# configmapRef: # …from a ConfigMap (name + key)
# name: jira-templates
# key: comment.tmpl
- name: mark-done
type: transition
enabled: true
transition: "Done" # transition name (case-insensitive) or numeric id
when: 'isPipelineRun() && event.State == "success"'-
Linking the issue: the target key (e.g.
PROJ-123) comes from thetekton.dev/tekton-events-relay.jira.issue-keyannotation. The TriggerBinding extracts it from the branch name or PR/MR title via a CEL overlay — no Tekton task required. Events without the annotation are skipped. -
comment: posts the rendered template on the issue.api_version: "2"(default) sends plain-text bodies;api_version: "3"sends Atlassian Document Format bodies (Cloud). The bodytemplateis a resolvable reference (valueinline orconfigmapRef: {name, key}); omit it for the default shipped in the templates ConfigMap (jira-comment.tmpl). -
transition: resolves the configuredtransitionagainst the issue's available transitions and applies it; if that transition isn't currently available, the action is a no-op rather than an error. -
Auth: Cloud uses email + API token (basic); Data Center sends the token as a bearer PAT. A Data Center instance fronted by an OAuth2 server (or a Cloud service-account OAuth credential) can instead use
auth.oauth2client credentials — sent asBearer, auto-refreshed before expiry;grant_typedefaults toclient_credentials(refresh_tokenalso supported).oauth2is not combinable withemailor a statictoken; the static token file is otherwise re-read per request (rotation without restart).insecure_skip_verify: truefor self-signed Data Center.
Outbound calls go through the shared retry policy (backoff + jitter, Retry-After aware). Failures are visible in /readyz, tekton_events_relay_notifier_retries_total and, for permanent errors with the DLQ enabled, replayable.
Getting started
Reference
SCM providers
Notifiers
Running in production
More