Codex の利用トークンをローカルで取りこぼしにくく収集し、Cloudflare Workers + D1 に送信して、日次レポートを Discord に通知するための小さな運用ツールです。
Codex Stop hook
-> local SQLite queue
-> Cloudflare Worker
-> D1
-> Cron Trigger
-> Discord Webhook
| Area | Choice |
|---|---|
| Local runtime | Python CLI, uv, python-dotenv |
| Local durability | SQLite queue |
| Cloud backend | Cloudflare Workers, D1, Cron Triggers |
| Notification | Discord Webhook |
| Client grouping | client_id |
- Features
- Report Example
- Requirements
- Quick Start
- Usage
- Codex Hook
- Multi-Device Setup
- Pricing
- Development
- Documentation
- Security Notes
- Codex の各ターン終了時に usage を収集
- ローカル SQLite に永続化し、送信失敗時は後で再送
- 複数デバイスを
client_idで集計 - Cloudflare Workers + D1 + Cron Triggers で無料枠に収まりやすい構成
- Discord へ日次レポートを送信
- ユーザー別、モデル別、ユーザー x モデル別の集計
- API Usage の概算料金と週次シェアをレポート
uvと.envベースのローカル運用
Codex usage report
Period: 2026-05-07 Asia/Tokyo
Total: 11,913,455 tokens
Input: 11,874,513
Cached input: 7,552
Output: 38,942
Reasoning output: 87
Estimated API usage: $60.5408
Sessions: 6
Week: 2026-05-04 - 2026-05-10
By client (daily tokens / estimated cost / weekly share):
- client-a: 11,913,455 / $60.5408 / 100.0%
By model:
- gpt-5.5: 11,913,455
- Python 3.11+
- uv
- Node.js 20+
- Cloudflare account
- Discord Webhook URL
- Codex with hook support enabled
Clone and install dependencies:
git clone <repo-url>
cd usage-notify
uv sync
npm installCreate local settings:
cp .env.example .envMinimum client settings:
USAGE_NOTIFY_CLIENT_ID=client-a
USAGE_NOTIFY_TOKEN=replace-with-shared-random-token
USAGE_NOTIFY_API_BASE_URL=https://usage-notify.<your-subdomain>.workers.dev
USAGE_NOTIFY_LOCAL_DB=/home/<user>/.local/share/usage-notify/events.db
USAGE_NOTIFY_CODEX_LOG=/home/<user>/.codex/log/codex-tui.log
ADMIN_TOKEN=replace-with-long-random-admin-token
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...Set up Cloudflare:
npx wrangler login
npx wrangler d1 create usage_notifyThe checked-in wrangler.toml uses a placeholder D1 database ID:
database_id = "replace-with-d1-database-id"Copy the database_id printed by npx wrangler d1 create usage_notify and replace the placeholder before running remote migrations or deploying. The generated output looks like this:
[[d1_databases]]
binding = "DB"
database_name = "usage_notify"
database_id = "<generated-d1-database-id>"After updating wrangler.toml, run:
npm run cf:migrate:remote
npx wrangler secret put CLIENT_TOKENS
npx wrangler secret put ADMIN_TOKEN
npx wrangler secret put DISCORD_WEBHOOK_URL
npm run cf:deployFor personal or small-team use, CLIENT_TOKENS can be a single shared token:
*=replace-with-shared-random-token
Install the Codex user hook:
scripts/install-codex-hookRestart Codex sessions after installing or changing hooks.
Collect and upload now:
uv run usage-notify flush --retry-nowCheck local queue status:
uv run usage-notify statusGenerate the remote D1 report:
uv run usage-notify cloudflare-report --date 2026-05-07Send the remote D1 report to Discord:
uv run usage-notify cloudflare-report --date 2026-05-07 --send-discordSuccessful Discord sends end with:
discord report sent
Test only the local Discord webhook path:
uv run usage-notify test-discordscripts/install-codex-hook configures a user-scope Codex Stop hook:
- creates
~/.local/bin/usage-notify-codex-hook-stop - enables
hooks = truein~/.codex/config.toml - writes
~/.codex/hooks.json
The hook runs at the end of each Codex turn, waits briefly for the matching usage line in ~/.codex/log/codex-tui.log, stores it in the local SQLite queue, and uploads it to the configured Worker.
Uninstall the hook from the current device:
scripts/uninstall-codex-hookThe uninstall script removes only this project’s hook command from ~/.codex/hooks.json and writes a backup before editing. It leaves hooks = true unchanged because other hooks may use it.
Use Git to share the project files. Do not commit or share .env, .venv/, node_modules/, or *.db.
On another device:
git clone <repo-url>
cd usage-notify
scripts/setup-deviceThen edit .env for that device:
USAGE_NOTIFY_CLIENT_ID=client-b
USAGE_NOTIFY_TOKEN=replace-with-shared-random-token
USAGE_NOTIFY_API_BASE_URL=https://usage-notify.<your-subdomain>.workers.devWith CLIENT_TOKENS=*=replace-with-shared-random-token, every device uses the same USAGE_NOTIFY_TOKEN; the report separates devices by USAGE_NOTIFY_CLIENT_ID.
If you want per-device tokens instead:
client-a=token1,client-b=token2
Estimated cost is calculated from input_tokens and output_tokens with model prices in wrangler.toml:
[vars]
MODEL_PRICES_JSON = '{"gpt-5.5":{"input":5,"output":30},"gpt-5.4-mini":{"input":0.75,"output":4.5}}'The Worker also includes default prices for common OpenAI models such as gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.3-codex, gpt-5.2, gpt-5.1, gpt-5, gpt-4.1, gpt-4o-mini, and o4-mini.
cached_input_tokens, non_cached_input_tokens, and reasoning_output_tokens are collected when they are present in the Codex usage log. Cached input discounts are only applied to the estimate when a model price includes cached_input; otherwise input is charged at the normal input price. The estimate does not currently account for Batch API discounts, long-context surcharges, or regional processing surcharges. Treat it as an operational estimate, not an invoice replacement.
Run Python tests:
uv run python -m unittest discover -s tests -vRun Cloudflare Worker tests:
npm run cf:testRun a local Worker:
npm run cf:migrate:local
npm run cf:devDeploy:
npm run cf:deploy- Keep
.env, Discord Webhook URLs,ADMIN_TOKEN, andCLIENT_TOKENSout of Git. - For personal use, a shared token is usually enough.
- For stricter separation, use one token per
client_id. - Local events are queued on disk, so protect the local database path if the device is shared.