Codemagic build finished notifications on your phone.

Get a High-priority push notification when a Codemagic build fails, and optionally a quiet one when it succeeds, using a short publishing script in codemagic.yaml.

build.finishedCI/CDBuilds & CI
How it works

Three steps to your first push

STEP 01

Install the recipe

One click in Studio gives you a personal webhook URL.

STEP 02

Paste your URL into Codemagic

Add it as a webhook for Build finished.

STEP 03

Get a push on your phone

With the text, sound and buttons from the recipe.

Setup

How to set up the Codemagic build finished webhook

  1. Click Install in Studio and sign in. The recipe is added to your account and you get a personal webhook URL.
  2. Codemagic has no outgoing webhooks, so copy the codemagic.yaml snippet from the setup notes below. It already contains your endpoint URL.
  3. Add its two parts to each workflow you want to hear about: a last script step, and a publishing script.
  4. Commit codemagic.yaml and run a build to test.
  5. Codemagic can email you or post to Slack, but it can't call a webhook when a build ends. Its [publishing scripts](https://docs.codemagic.io/yaml-distributing/post-publish/) run after every build, pass or fail, so this recipe uses one to send the result to JustPush.
  6. Add both parts below to each workflow in `codemagic.yaml` you want to hear about. The **last** script step marks the build as successful; the publishing script checks for that mark and posts the result. Replace `Acme Shop` with your app's name.
  7. ```yaml
  8. workflows:
  9. ios-release:
  10. name: iOS release
  11. environment:
  12. vars:
  13. JUSTPUSH_APP_NAME: "Acme Shop"
  14. scripts:
  15. # ... your existing build steps ...
  16. name: Mark build successful
  17. script: touch ~/SUCCESS
  18. publishing:
  19. scripts:
  20. name: Notify JustPush
  21. script: |
  22. if [ -f "$HOME/SUCCESS" ]; then STATUS=success; else STATUS=failure; fi
  23. jq -n \
  24. --arg status "$STATUS" \
  25. --arg app "${JUSTPUSH_APP_NAME:-$CM_REPO_SLUG}" \
  26. --arg workflow "$CM_WORKFLOW_NAME" \
  27. --arg build_number "$BUILD_NUMBER" \
  28. --arg branch "$CM_BRANCH" \
  29. --arg tag "$CM_TAG" \
  30. --arg commit "$CM_COMMIT" \
  31. --arg commit_message "$(git -C "$CM_BUILD_DIR" log -1 --pretty=%s 2>/dev/null)" \
  32. --arg url "https://codemagic.io/app/$CM_PROJECT_ID/build/$CM_BUILD_ID" \
  33. '{event: "build.finished", status: $status, app: $app, workflow: $workflow,
  34. build_number: $build_number, branch: $branch, tag: $tag, commit: $commit,
  35. commit_message: $commit_message, url: $url}' \
  36. | curl -s -m 10 -X POST -H "Content-Type: application/json" --data @- "your webhook URL" || truehttps://••••••••/•••••••• your personal URL, shown after install
  37. ```
  38. Only failed builds push by default (High priority). To also get a quiet push when a build succeeds, add `?all=1` to the URL in the snippet:
  39. ```
  40. your webhook URL?all=1
  41. ```
  42. Notes:
  43. Codemagic doesn't run publishing scripts for builds that are **canceled or time out**, so those don't push.
  44. The success mark only covers your script steps. If a built-in publisher such as App Store Connect or Google Play fails, this push can't tell; Codemagic's own email or Slack notifications cover that.
  45. The snippet uses `jq` and `git`, as Codemagic's own publishing examples do. The `|| true` keeps a JustPush hiccup from failing your build.
  46. The request is unsigned (`verify: none`), so keep your endpoint URL private.
The code

What runs when the webhook arrives

Studio calls handleRequest(request) with the incoming webhook and sends the message it returns. It's yours after install; change anything.

transform.js
1// Codemagic → build finished
2// Fires when the "Notify JustPush" publishing script from INSTALL.md reports a
3// "build.finished" event. Codemagic has no outgoing webhooks, so the snippet sends it.
4// Docs: https://docs.codemagic.io/yaml-distributing/post-publish/
5
6// The snippet reports "success" or "failure". Failures push by default; successes need ?all=1.
7const STATUSES = {
8 failure: { emoji: "❌", verb: "failed", priority: 1, always: true }, // broken build: needs you soon
9 success: { emoji: "✅", verb: "succeeded", priority: -1, always: false }, // good news, only with ?all=1
10}
11
12// Look a key up in a table without tripping over inherited names like "toString".
13function pick(table, key, fallback) {
14 return typeof key === "string" && Object.prototype.hasOwnProperty.call(table, key) ? table[key] : fallback
15}
16
17// The snippet sends plain strings; treat blanks as missing.
18function text(value) {
19 if (typeof value === "number" && Number.isFinite(value)) return String(value)
20 return typeof value === "string" && value.trim() ? value.trim() : null
21}
22
23// Only link to real http(s) URLs.
24function link(url, cta) {
25 return typeof url === "string" && /^https?:\/\//.test(url) ? [{ cta, url }] : []
26}
27
28function handleRequest(request) {
29 const event = request.body && typeof request.body === "object" ? request.body : {}
30
31 // Guard: only this recipe's event, so other snippets on this integration stay quiet.
32 if (event.event !== "build.finished") {
33 console.log(`Ignoring Codemagic event "${event.event ?? "unknown"}"`)
34 return null
35 }
36
37 // ?all=1 on the URL in the snippet also sends quiet pushes for successful builds.
38 const all = ["1", "true", "yes"].includes(String(request.query?.all ?? "").toLowerCase())
39 const status = pick(STATUSES, event.status, null)
40 if (!status || (!status.always && !all)) {
41 console.log(`Skipping Codemagic build status "${event.status ?? "unknown"}"`)
42 return null
43 }
44
45 const app = text(event.app) ?? "Your app"
46 const number = text(event.build_number)
47 const where = text(event.branch) ?? (text(event.tag) ? `tag ${text(event.tag)}` : null)
48 const subject = (text(event.commit_message) ?? "").split("\n")[0]
49
50 return {
51 title: `${status.emoji} ${app} build${number ? ` #${number}` : ""} ${status.verb}`,
52 message: [text(event.workflow), where].filter(Boolean).join(" · ") + (subject ? ` — ${subject}` : "") || "Codemagic build finished",
53 topic: "Codemagic",
54 priority: status.priority,
55 buttons: link(event.url, "View build"),
56 }
57}
Payload

The Codemagic build.finished webhook

This is what Codemagic sends to your URL for build finished. It is a sample, trimmed to the fields recipes use.

build.finished · sample.json
1{
2 "app": "Acme Shop",
3 "tag": "",
4 "url": "https://codemagic.io/app/5f1a2b3c4d5e6f7a8b9c0d1e/build/64f0a1b2c3d4e5f6a7b8c9d0",
5 "event": "build.finished",
6 "branch": "main",
7 "commit": "564b61ebdd403d28b5dc616a12ce160b91585b5b",
8 "status": "failure",
9 "workflow": "iOS release",
10 "build_number": "57",
11 "commit_message": "Add checkout screen"
12}
FAQ

Codemagic build finished notifications: questions

Does this work on iPhone and Android?

Yes. Install the JustPush app from the App Store or Google Play and sign in. Every phone signed in to your account gets the notification.

Do I need to write code?

No. Install the recipe in Studio, paste your webhook URL into Codemagic and you are done. The code is there if you want to change the text, the sound or the buttons.

Can I change what the notification says?

Yes. After install the recipe's code is yours. Edit it in Studio and test it against the sample payload before you save.

What does it cost?

JustPush is free for 30 days. After that it's $19.99 a year, or $39.99 once. There is no extra charge for recipes.

Ready when you are

Codemagic on your phone in two minutes.

Install the recipe, paste one URL, done. Free for 30 days, no credit card required.