Documentation menu

Deploy notifications

Tell your team when CI ships — one curl in your pipeline, routed to chat, with commit SHAs as idempotency keys.

Deploy notifications are the easiest win with EventSend: your CI already runs shell commands, so it already knows how to send an event.

Deploying on Vercel? Skip the pipeline work entirely. A Vercel source emits deploy-started, deploy-succeeded, deploy-failed and friends with no changes to your build, and the routing table below matches those names as-is. The curl recipe here is still the answer for anything only your pipeline knows — migrations run, artifacts published, smoke tests passed — and the two work side by side.

1. Store the token

Create a route in the dashboard (or reuse an existing one) and copy its es_ token into your CI provider's secrets — EVENTSEND_TOKEN in GitHub Actions, a masked variable in GitLab, and so on. Never commit it.

2. Send an event when the deploy finishes

curl -X POST https://eventsend.io/api/events \
-H "Authorization: Bearer $EVENTSEND_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"event\": \"deploy-finished\",
\"message\": \"Deployed $GITHUB_REF_NAME to production\",
\"level\": \"success\",
\"icon\": \"🚀\",
\"unique_key\": \"deploy-$GITHUB_SHA\",
\"payload\": {\"commit\": \"$GITHUB_SHA\", \"actor\": \"$GITHUB_ACTOR\"}
}"

The unique_key is doing real work here. CI jobs get re-run — by a flaky test, by a manual retry, by someone clicking "re-run all jobs" — and without it every re-run posts another "Deployed to production" message. Keyed on the commit SHA, the second attempt returns 409 Conflict and stays quiet.

3. Wire it into GitHub Actions

- name: Notify EventSend
if: always()
env:
EVENTSEND_TOKEN: ${{ secrets.EVENTSEND_TOKEN }}
STATUS: ${{ job.status }}
run: |
if [ "$STATUS" = "success" ]; then LEVEL=success; ICON="🚀"; else LEVEL=error; ICON="🔥"; fi
curl -sS -X POST https://eventsend.io/api/events \
-H "Authorization: Bearer $EVENTSEND_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"event\": \"deploy-$STATUS\",
\"message\": \"Deploy of ${GITHUB_REF_NAME} finished with status ${STATUS}\",
\"level\": \"$LEVEL\",
\"icon\": \"$ICON\",
\"unique_key\": \"deploy-${GITHUB_SHA}-${STATUS}\",
\"payload\": {\"commit\": \"${GITHUB_SHA}\", \"actor\": \"${GITHUB_ACTOR}\"}
}"

if: always() matters — without it the step is skipped exactly when you most want to hear about it. Sending deploy-success and deploy-failure as different event names, at different levels, lets you route them differently in the next step.

4. Route it

Create a topic connected to your CI route and your chat destination, with the event pattern deploy-*.

To keep routine deploys out of an on-call channel while still being told about failures, use two topics on the same route:

Topic Pattern Minimum level Destination
Deploys deploy-* default #engineering
Deploy failures deploy-* error Telegram / on-call

The minimum level and the pattern are combined with AND, so the second topic only fires on the failure events.

Do you need this at all?

If all you want is "tell me when a push lands, a PR is merged, or CI fails", the GitHub source does it with no pipeline changes: paste a URL and a secret into your repository's webhook settings and you're done.

This recipe is for the events only your pipeline knows — a deploy that finished after the smoke tests passed, a migration that ran, a canary that was promoted. GitHub reports that a workflow completed; only your workflow knows what it actually did. The two work well together: the source covers repository activity, and a curl covers the moments you choose.

Notes

  • Keep payload small and structured — commit, branch, actor, duration. It has a 4096-byte limit and it's what a webhook destination will consume downstream.
  • message can be up to 500 characters; put the human-readable summary there rather than in the payload.
  • If your CI can't reach the public internet, EventSend can't be reached either — send the event from a step that runs on a networked runner.