Developer guides
GitHub Actions: Notify on Failure with Team Chat Alerts

A failed build is useful information only when the right people see it. GitHub Actions notifications can put the result beside your team’s conversation, with enough context to decide whether to investigate or keep working.
This guide sends the result of a GitHub Actions job to a Fryll channel using curl. You will create a conversation token, store it as a repository secret, and add a notification step that runs after success or failure. No separate Fryll GitHub app is required.
To notify only when a job fails: put the notification step after your build and use if:${{ failure() }}. To report both successful and failed jobs while skipping cancellation, use if:${{ !cancelled() }}, as in the complete example below.
You’ll need: a Fryll workspace and conversation, permission to add GitHub repository secrets, and a workflow using a runner with Bash and curl. The complete example below is for a Node.js project with a package lockfile and a build script.
Choose what deserves a notification
Start with one useful event: the build on your main branch. Include the repository, job, result, branch, commit, and workflow link. The link lets a teammate investigate without pasting a whole log into chat.
For a small team, a dedicated #builds or #deploys channel is a reasonable starting point. If successful runs become noise, keep failures and meaningful release events. Fryll receives the result your workflow sends; it does not determine whether a build or deployment succeeded.
GitHub also offers its own email and web notifications. Those are useful for individuals. This example puts the outcome into a shared team conversation. See GitHub’s workflow notification documentation for the native options.
Create a conversation token and a repository secret
- In Fryll, open the destination channel or DM.
- Open its Details and expand Your conversation token.
- Generate a token and copy it. The secret is displayed once.
- In your GitHub repository, open Settings → Secrets and variables → Actions.
- Add a repository secret named
FRYLL_TOKENwith that value.
The token belongs to the person who generated it and posts as them by default. It is scoped to that conversation. Keep the token in the secret store, rather than a workflow file, screenshot, or log. You can rotate or revoke it in Fryll; after rotation, update the repository secret.
If you use organization or environment secrets instead, check the repository and environment access rules. GitHub documents the setup and availability rules in Using secrets in GitHub Actions.
Add a workflow that reports success and failure
Save this as .github/workflows/build-and-notify.yml. It checks out the code, installs dependencies, builds, and reports the build job’s result. Adapt the branch, Node version, package manager, and build command to your project. If you already have a workflow, copy just the final notification step into the job you want to report.
name: Build and notify
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
- run: npm ci
- run: npm run build
- name: Send result to Fryll
if: ${{ !cancelled() }}
continue-on-error: true
timeout-minutes: 1
env:
FRYLL_TOKEN: ${{ secrets.FRYLL_TOKEN }}
JOB_STATUS: ${{ job.status }}
JOB_NAME: ${{ github.job }}
REPOSITORY: ${{ github.repository }}
BRANCH_NAME: ${{ github.ref_name }}
COMMIT_SHA: ${{ github.sha }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
shell: bash
run: |
if [[ -z "$FRYLL_TOKEN" ]]; then
printf '::notice::Skipping Fryll notification: FRYLL_TOKEN is unavailable.\n'
exit 0
fi
message=$(printf '%s / %s: %s\nBranch: %s\nCommit: %.7s\nRun: %s' \
"$REPOSITORY" "$JOB_NAME" "$JOB_STATUS" \
"$BRANCH_NAME" "$COMMIT_SHA" "$RUN_URL")
printf '%s' "$message" | curl \
--fail --silent --show-error \
--connect-timeout 5 --max-time 15 \
--output /dev/null \
--header 'Content-Type: text/plain; charset=utf-8' \
--data-binary @- \
"https://api.fryll.com/in/$FRYLL_TOKEN"The example uses the versions documented by actions/checkout and actions/setup-node at publication. Follow your team’s action-version or commit-pinning policy, and check runner requirements if you self-host runners.
Why these options matter
!cancelled()allows the step after success or failure, while skipping a cancelled job. A normal step without a status condition is skipped after an earlier failure.continue-on-error: truekeeps a notification delivery problem from failing an otherwise successful build. It does not make an earlier failed build successful.- The connection and request timeouts bound how long the HTTP request can wait.
- Context values enter through environment variables. Branch names and other variable content are data, not shell program text.
- Plain text is read from stdin with
--data-binary @-, so quotes and newlines do not need manual JSON escaping.
For failures only, change the condition to the following. Keep the notification after the work it reports.
if: ${{ failure() }}GitHub explains these conditions in its expression reference. Its script injection guidance also explains why variable context should not be inserted directly into shell code.
Report a deployment only after it is verified
A successful build is not the same as a successful deployment. To send deployment notifications, put the notification after your deployment command and a health or rollout check. If the provider only acknowledges a deployment request, wait for its final result before saying the release is live.
Use a separate deployment job if that matches your workflow. The example reports one job’s status, not a summary of every job in a multi-job workflow. A deployment job should include its environment in the message so “staging succeeded” cannot be mistaken for “production succeeded.”
For a longer release, you can post a start message and use the returned message ID for a follow-up thread reply. The notification guide describes the thread_id field. The first version of your integration can simply send one final result.
If the notification does not arrive
Open the run and inspect the notification step first. An unavailable secret produces a notice and skips posting. GitHub normally withholds repository secrets from fork-origin pull requests and Dependabot-triggered workflows. Do not change to a privileged trigger simply to make a secret available to untrusted code.
- 400: check that the message is non-empty and the body matches the content type.
- 404: check whether the token is valid, still active, and usable for its conversation.
- 429: reduce the posting rate. A token permits 60 requests per minute.
- A timeout: the message may already have been accepted. Check the conversation before rerunning the request.
The example does not automatically retry text posts because the inbound endpoint does not provide a message idempotency key. For more response details, use the API error reference.
Once this first signal is useful, add another. The same endpoint can report backups and local commands; the Bash notification guide shows a wrapper that preserves the original command’s exit status.