Skip to content

Using the Action

- uses: jtprogru/notiflow@v2
if: always()
with:
bot_token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
chat_id: ${{ secrets.TELEGRAM_CHAT_ID }}
status: ${{ job.status }}

Three things about that snippet are load-bearing:

if: always() — steps do not run after a failed step unless you say so, and the failure is exactly the case you wanted a message about. Use if: always() for “tell me either way” and if: failure() for “only when it breaks”.

status — must be passed. Composite-action input defaults cannot read the job context, so there is no way for notiflow to default it to job.status on your behalf.

bot_token — comes from a secret. notiflow masks it in the step log the moment it starts, but a token pasted into the workflow file is already in your git history.

job.status is the status of the job the step lives in. To notify about a job that ran earlier, add a reporting job and read needs:

jobs:
build:
runs-on: ubuntu-latest
steps:
- run: make ci
notify:
needs: [build]
if: always()
runs-on: ubuntu-latest
steps:
- uses: jtprogru/notiflow@v2
with:
bot_token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
chat_id: ${{ secrets.TELEGRAM_CHAT_ID }}
status: ${{ needs.build.result }}

notify_on is a comma-separated list of statuses that should produce a message. The default is success,failure,cancelled — everything except skipped.

notify_on: failure,cancelled # only bad news
notify_on: any # everything, including skipped

When the reported status is not in the list, notiflow exits 0 without contacting Telegram, and sets ok=false with an empty message_id.

Three ways, in order of precedence.

message is verbatim: no placeholders, no escaping, exactly what you wrote.

message: "Nightly build finished"

message_template is rendered with placeholders:

message_template: |
{{.StatusEmoji}} *{{.Workflow}}* — {{.Status}}
`{{.Repo}}` @ `{{.ShortSha}}`
[run]({{.RunUrl}})

template_success, template_failure, template_cancelled and template_skipped override message_template for one status:

message_template: "{{.StatusEmoji}} {{.Workflow}}: {{.Status}}"
template_failure: |
❌ *{{.Workflow}}* broke on `{{.Branch}}`
{{.Actor}} pushed `{{.ShortSha}}`
[logs]({{.RunUrl}})
- uses: jtprogru/notiflow@v2
id: notify
with:
bot_token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
chat_id: ${{ secrets.TELEGRAM_CHAT_ID }}
status: ${{ job.status }}
- if: steps.notify.outputs.ok != 'true'
run: echo "notification failed: ${{ steps.notify.outputs.error }}"

By default a delivery failure does not fail the job: notiflow’s opinion about Telegram should not overwrite the result your build actually produced. If the notification is part of the contract, opt in:

fail_on_error: true

v2 sends to exactly one chat per step. Use a matrix when you want several:

jobs:
notify:
strategy:
matrix:
chat: ['-1001111111111', '-1002222222222']
runs-on: ubuntu-latest
steps:
- uses: jtprogru/notiflow@v2
with:
bot_token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
chat_id: ${{ matrix.chat }}
status: ${{ needs.build.result }}

Each chat gets its own step in the UI, its own outputs and its own pass or fail, which the comma-separated form in v1.5 could not give you. See the migration guide for the reasoning.