Get a desktop or Slack notification when Codex finishes a turn: notify and tui.notifications

Codex Published:

Run a program when Codex CLI completes a turn with the notify setting, the JSON it receives, and how tui.notifications filters terminal notifications, with macOS/Linux examples.

Verified on Sep 10, 2026 These tools change quickly. Please also check the latest official documentation.
Contents
  1. notify: launching an external program
    1. macOS example
    2. Linux example
    3. Sending to Slack
  2. tui.notifications: controlling terminal notifications
  3. Summary

Hand Codex a longer task, switch to something else, and you can easily miss the moment it finishes. Codex CLI offers two mechanisms: notify, which launches an external program when a turn completes, and tui.notifications, which controls the terminal's own notifications.

In short: write an argv array such as notify = ["python3", "/path/to/notify.py"], and Codex runs that program with a single JSON argument at the end of every turn. From there you can post a desktop notification, hit a Slack webhook, or anything else.

KEY POINT

What you will learn

  • How to write notify and which JSON fields the program receives
  • Filtering terminal notifications by type, condition, and method with tui.notifications
  • Desktop notification scripts for macOS and Linux

notify: launching an external program

In ~/.codex/config.toml, list the command as an array. Unset means disabled.

notify = ["python3", "/Users/you/.codex/notify.py"]

The program receives one JSON string as its argument and should exit 0 on success. The main fields:

FieldContents
typeevent type; currently agent-turn-complete
thread-idthe session (thread) identifier
turn-idthe turn identifier
cwdthe working directory
input-messagesthe user messages for that turn
last-assistant-messagethe assistant's final output text

用語解説

Turn: one round from you sending an instruction to Codex finishing its response, including any command runs and approvals in between. notify fires at the end of that unit.

macOS example

Using terminal-notifier (installable with Homebrew):

#!/usr/bin/env python3
import json, subprocess, sys

payload = json.loads(sys.argv[1])
if payload.get("type") != "agent-turn-complete":
    sys.exit(0)
title = "Codex: turn complete"
body = (payload.get("last-assistant-message") or "")[:120]
subprocess.run(["terminal-notifier", "-title", title, "-message", body, "-group", payload.get("thread-id", "")])

Linux example

With notify-send you can skip the script entirely; the sample configuration shows this form.

notify = ["notify-send", "Codex"]

The JSON string then becomes the notification body as is. Insert a script like the one above if you want it tidier.

Sending to Slack

For a Slack Incoming Webhook, have the script trim last-assistant-message and POST it. Read the webhook URL from an environment variable rather than writing it into config.toml.

last-assistant-message contains your work

Passing last-assistant-message straight into a notification sends file names and code fragments to an external service. For Slack and similar, truncate to a few dozen characters or send only a "done" marker.

tui.notifications: controlling terminal notifications

The terminal's own notifications are configured under [tui].

[tui]
# true / false, or a list of types
notifications = ["agent-turn-complete", "approval-requested"]
# unfocused (default) | always
notification_condition = "unfocused"
# auto (default) | osc9 | bel
notification_method = "auto"
KeyValuesMeaning
notificationstrue / false / liston by default; restrict to agent-turn-complete and approval-requested
notification_conditionunfocused / alwaysdefault fires only when the terminal is not focused
notification_methodauto / osc9 / belauto prefers OSC 9 when the terminal supports it, else BEL (\x07)

notify and tui.notifications are independent. For external notifications only, set notifications = false; to still hear about pending approvals, keep approval-requested in the list.

The overall structure of the config file is covered in the hub article Configuring Codex with config.toml, and when approvals are requested in Codex approval modes and sandbox settings.

Summary

  • notify is an argv array, run with one JSON argument on every agent-turn-complete
  • The JSON carries type, thread-id, turn-id, cwd, input-messages, and last-assistant-message
  • terminal-notifier on macOS or a bare notify-send on Linux is enough to start
  • Truncate the body before sending it to external services and keep webhook URLs in environment variables
  • Terminal notifications are separate: [tui] notifications, notification_condition, notification_method

FAQ

Which events does notify receive?
Currently agent-turn-complete. The program gets one JSON argument with type, thread-id, turn-id, cwd, input-messages, and last-assistant-message.
Can I silence terminal notifications but keep notify?
Yes. Set notifications = false under [tui]; notify is configured independently.
Does notify work with the IDE extension?
According to the notifications page, you configure notify on the Codex host the extension connects to.

Primary sources

This article was drafted by AI from official documentation and reviewed by the site operator before publishing. Found a mistake? Let us know via the contact page.