Braintrust alert notifications on your phone.

Get a push notification when a Braintrust log alert matches logs or a time window alert fires, recovers or fails to evaluate.

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 Braintrust

Add it as a webhook for Log alert.

STEP 03

Get a push on your phone

With the text, sound and buttons from the recipe.

Setup

How to set up the Braintrust log alert webhook

  1. Click Install in Studio and sign in. The recipe is added to your account and you get a personal webhook URL.
  2. In Braintrust, go to your project's Settings > Alerts and create an alert of type Log alert.
  3. Choose the Webhook action and paste your webhook URL as the URL.https://••••••••/•••••••• your personal URL, shown after install
  4. Test the alert (where available) and save.
  5. In your Braintrust project, go to **Settings → Alerts** and create a **log alert** (a filter over your logs) or a **time window alert** (a metric against a threshold). Choose the **Webhook** action with the URL above.
  6. Log alerts need a successful **Test** before they can be saved; test deliveries show up with a `[Test]` prefix. Braintrust batches matches per interval, so one push can cover many logs.
  7. Braintrust doesn't sign webhooks, 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// Braintrust → log alert or time window alert
2// Fires when a Braintrust alert with a Webhook action matches logs (log alert) or
3// crosses its threshold (time window alert).
4// Docs: https://www.braintrust.dev/docs/admin/automations/alerts
5
6// Time window transitions: label and priority. "scheduled" doesn't push.
7const TRANSITIONS = {
8 firing: { emoji: "🔥", priority: 1 },
9 renotify: { emoji: "🔥", priority: 1, suffix: " (still firing)" },
10 recovery: { emoji: "✅", priority: -1, prefix: "Recovered: " },
11 evaluation_failed: { emoji: "⚠️", priority: 0, suffix: " (evaluation failed)" },
12 evaluation_recovered: { emoji: "✅", priority: -1, suffix: " (evaluation working again)" },
13}
14
15// Look a key up in one of the tables above, ignoring inherited names like "toString".
16function pick(table, key, fallback) {
17 return Object.prototype.hasOwnProperty.call(table, key) ? table[key] : fallback
18}
19
20// Only link to real http(s) URLs.
21function link(url, cta) {
22 return typeof url === "string" && /^https?:\/\//.test(url) ? [{ cta, url }] : []
23}
24
25function handleRequest(request) {
26 const body = request.body || {}
27 const automation = body.automation
28 const details = body.details ?? {}
29 const test = details.is_test === true ? "[Test] " : ""
30
31 // Guard: only log and time window alerts; environment updates have their own recipe.
32 if (!automation || !["logs", "windowed"].includes(automation.event_type)) {
33 console.log(`Ignoring Braintrust "${automation?.event_type ?? "unknown"}" webhook`)
34 return null
35 }
36
37 const project = body.project?.name
38 const name = automation.name ?? "Braintrust alert"
39
40 if (automation.event_type === "logs") {
41 const count = Number(details.count)
42 return {
43 title: `${test}🚨 ${name}`,
44 message:
45 (Number.isFinite(count) ? `${count} log${count === 1 ? "" : "s"} matched` : "Logs matched") +
46 (project ? ` in ${project}` : ""),
47 topic: "Braintrust",
48 priority: 1, // High — logs matched a filter you care about
49 buttons: [...link(details.related_logs_url, "View logs"), ...link(automation.url, "View alert")],
50 }
51 }
52
53 const transition = pick(TRANSITIONS, details.transition, null)
54
55 if (!transition) {
56 console.log(`Skipping time window transition "${details.transition}"`)
57 return null
58 }
59
60 const condition = automation.threshold?.policy?.condition
61 const symbol = { gt: ">", gte: "≥", lt: "<", lte: "≤" }[condition?.operator] ?? ""
62 const reading = details.value != null
63 ? `Value ${details.value}` + (condition?.threshold != null ? ` (threshold ${symbol} ${condition.threshold})`.replace(" ", " ") : "")
64 : details.message
65
66 return {
67 title: `${test}${transition.emoji} ${transition.prefix ?? ""}${name}${transition.suffix ?? ""}`,
68 message: [reading, project].filter(Boolean).join(" · ") || "Alert changed state",
69 topic: "Braintrust",
70 priority: transition.priority,
71 buttons: link(automation.url, "View alert"),
72 }
73}
Payload

The Braintrust logs webhook

This is what Braintrust sends to your URL for log alert. It is a sample, trimmed to the fields recipes use.

logs · sample.json
1{
2 "details": {
3 "count": 5,
4 "is_test": false,
5 "message": "High-Priority Factuality: 5 logs triggered this alert. You won't be notified again for at least 1 hour.",
6 "time_end": "2026-09-29T11:00:00.000Z",
7 "time_start": "2026-09-29T10:00:00.000Z",
8 "related_logs_url": "https://www.braintrust.dev/app/acme/p/support-bot/logs?search=..."
9 },
10 "project": {
11 "id": "proj_456",
12 "name": "support-bot"
13 },
14 "automation": {
15 "id": "c5b32408-8568-4bff-9299-8cdd56979b67",
16 "url": "https://www.braintrust.dev/app/acme/p/support-bot/configuration/alerts?aid=c5b32408",
17 "name": "High-Priority Factuality",
18 "event_type": "logs",
19 "btql_filter": "metadata.priority = 0 AND scores.Factuality < 0.9",
20 "description": "Alert on factuality scores for priority logs",
21 "interval_seconds": 3600
22 },
23 "organization": {
24 "id": "org_123",
25 "name": "acme"
26 }
27}
FAQ

Braintrust alert 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 Braintrust 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

Braintrust on your phone in two minutes.

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