todoki

使い方

アプリで発行した URL に HTTP POST するだけです。このページでは payload の書き方・送信例と、URL が途絶えたら知らせる「途絶監視」を説明します。

以下の例の $TODOKI_URL は、アプリの URL 一覧からコピーしたあなたの Webhook URLhttps://hook.todoki.dev/hook/<id> 形式)に置き換えてください。URL 末尾の id が秘密鍵の役割を果たすため、URL は公開リポジトリ等に書かず、CI の secret として扱ってください

最小の例

curl -X POST "$TODOKI_URL" \
  -H 'content-type: application/json' \
  -d '{"title":"デプロイ完了","body":"v1.2.3 を本番反映"}'
# => 202 {"ok":true}

JSON を組み立てるのも面倒なら、プレーンテキストを投げるだけでも通知になります(叩くだけモード):

curl -X POST "$TODOKI_URL" -H 'content-type: text/plain' -d 'ビルド終わったよ'

payload フィールド

JSON で送る場合、以下のフィールドが使えます。すべて省略可能です。

フィールド意味
title通知タイトル"ビルド失敗"
body本文"main ブランチ"
priority優先度。time-sensitive(即時)/ normal / low"time-sensitive"
click通知タップ時に開く URL"https://example.com/build/123"

適用の優先順位は「payload の明示フィールド > URL に設定したテンプレート > 既定値」です。

タップで URL を開く

curl -X POST "$TODOKI_URL" -H 'content-type: application/json' \
  -d '{"title":"CI 失敗","body":"ログを開く","click":"https://example.com/ci/run/123"}'

緊急度を上げる

curl -X POST "$TODOKI_URL" -H 'content-type: application/json' \
  -d '{"title":"アラート","body":"CPU 90%","priority":"time-sensitive"}'

言語・ツール別の送信例

ただの HTTP POST なので、HTTP を送れるものなら何からでも通知できます。下のタブで言語を切り替えてください。$TODOKI_URL はあなたの Webhook URL に置き換えます(curl・JavaScript・Python はアプリの「テスト送信」から、その URL 入りのスニペットをそのままコピーできます)。

curl -X POST "$TODOKI_URL" \
  -H 'content-type: application/json' \
  -d '{"title":"デプロイ完了","body":"v1.2.3 を本番反映"}'

JSON すら不要なら、プレーンテキストを投げるだけでも通知になります(叩くだけモード):

curl -X POST "$TODOKI_URL" -H 'content-type: text/plain' -d 'ビルド終わったよ'

ブラウザ / Deno / Bun / Node 18+(標準の fetch)

fetch("$TODOKI_URL", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ title: "デプロイ完了", body: "v1.2.3 を本番反映" }),
});

requests を使う場合

import requests
requests.post("$TODOKI_URL", json={"title": "デプロイ完了", "body": "v1.2.3 を本番反映"})

標準ライブラリだけ(依存なし)

from urllib.request import urlopen, Request
import json
body = json.dumps({"title": "デプロイ完了", "body": "v1.2.3 を本番反映"}).encode()
urlopen(Request("$TODOKI_URL", data=body, headers={"content-type": "application/json"}))

標準 https モジュール(依存なし)

const https = require("https");
const data = JSON.stringify({ title: "デプロイ完了", body: "v1.2.3 を本番反映" });
const req = https.request("$TODOKI_URL", {
  method: "POST",
  headers: { "content-type": "application/json", "content-length": data.length },
});
req.write(data); req.end();
package main

import ("net/http"; "strings")

func main() {
  body := `{"title":"デプロイ完了","body":"v1.2.3 を本番反映"}`
  http.Post("$TODOKI_URL", "application/json", strings.NewReader(body))
}
require "net/http"; require "json"
uri = URI("$TODOKI_URL")
Net::HTTP.post(uri, {title: "デプロイ完了", body: "v1.2.3 を本番反映"}.to_json,
               "content-type" => "application/json")
Invoke-RestMethod -Uri "$TODOKI_URL" -Method Post -ContentType "application/json" `
  -Body '{"title":"デプロイ完了","body":"v1.2.3 を本番反映"}'
http POST "$TODOKI_URL" title="デプロイ完了" body="v1.2.3 を本番反映"

JSON すら不要なら、どの言語でもプレーンテキストを本文に投げるだけで通知になります(content-type: text/plain)。

URL ごとのテンプレート

送信側のサービスが決まった形の JSON を送ってくる場合(例: {"repo":"...","msg":"..."})、URL 側にテンプレートを設定しておけば、送信側は生の JSON を送るだけで整形された通知になります。※ テンプレートの登録 UI はアプリで今後対応予定です(下記はその際の書き方)。

# URL に設定したテンプレート: {"title":"{{repo}}","body":"{{msg}}"}

curl -X POST "$TODOKI_URL" -H 'content-type: application/json' \
  -d '{"repo":"todoki/backend","msg":"build passed"}'
# => 通知: タイトル「todoki/backend」 本文「build passed」

{{a.b}} のようなドット記法でネストした値も参照できます。

CI からの利用例

GitHub Actions

# Webhook URL はリポジトリの Secrets に TODOKI_URL として登録しておく
- name: Notify
  if: always()
  run: |
    curl -sf -X POST "${{ secrets.TODOKI_URL }}" \
      -H 'content-type: application/json' \
      -d "{\"title\":\"${{ github.repository }}\",\"body\":\"${{ job.status }}: ${{ github.workflow }}\"}"

シェルスクリプト(長時間処理の完了通知)

./long-running-job.sh; \
curl -X POST "$TODOKI_URL" -H 'content-type: text/plain' \
  -d "job finished: exit=$?"

途絶監視(ハートビート)

todoki はふだん「届いた通知」を知らせますが、途絶監視はその逆 —— 来るはずの通知が来なくなったこと(沈黙)そのものを知らせます。毎朝動くはずのバッチ、定期送信のセンサー、cron が止まったことに気づくための死活監視(dead man's switch)です。

通常の通知はそのまま届きます 途絶監視を ON にしても、いつもの通知の受信には影響しません。これは「来なかったこと」を検知する追加の見張りで、通知を止める機能ではありません。無料で、通知の月間上限も消費しません。

設定する

途絶監視は URL ごとに、アプリの URL 詳細画面から設定します。

  1. アプリで対象の URL を開き、「途絶を監視」を ON にします。
  2. 監視間隔を選びます(15分 / 1時間 / 6時間 / 1日 / 1週間 から。既定は 1日)。
URL 詳細画面で「途絶を監視」を ON にし、監視間隔(15分〜1週間)のチップが表示された状態
「途絶を監視」を ON にして監視間隔を選ぶ

「この間隔より長くその URL が叩かれなかったら」途絶とみなして通知します。

途絶通知を出さないようにするには

答えはシンプルで、監視間隔の内に、最低 1 回その URL へ POST し続けることです。todoki はその URL への POST を「生存の合図(心拍)」として扱い、POST が届くたびにタイマーをリセットします。間隔を超えて沈黙が続いたときだけ、途絶として通知します。

コツ:普段の通知そのものを心拍にする 普段の通知 POST も心拍としてカウントされます。たとえば「毎日成功時に完了通知を送るバッチ」なら、その通知がそのまま心拍になります。監視間隔は、その送信の正常な周期より少し長めに設定してください(毎日のバッチなら 1日)。ジョブが落ちて POST が届かなければ、間隔超過で途絶通知が届きます。
# 毎日 6:00 に動くジョブ。成功したら todoki に POST(この POST が心拍になる)
0 6 * * * /path/to/job.sh && curl -fsS -X POST "$TODOKI_URL" \
  -H 'content-type: text/plain' -d 'daily job ok' >/dev/null
やりがちな失敗 「途絶させないため」だけに数分おきの ping を撃つと、その ping 自体が毎回 1 通の通知になり、鳴り続けます。心拍は意味のある送信に結びつけるのが基本です。なお URL をミュートすると途絶通知も止まります(アプリ内のバッジだけが残る)ので、ミュートは「途絶通知を黙らせる」手段にはなりません。

途絶すると、何回通知が来るのか

鳴り続けません。1 回の途絶につき最大 4 通(途絶 1 + 追い通知 2 + 復帰 1)で打ち止めです。

  1. 1 / 3
    途絶を検知 — 「『ラベル』からの通知が途絶えています。」を 1 回。 設定間隔を過ぎた直後(約 5 分おきに見張っているため、タイミングは最大 5 分ほど前後します)
  2. 2 / 3
    追い通知 — 「まだ途絶えています。」 最初の検知から約 6 時間後
  3. 3 / 3
    追い通知(最後) — 「まだ途絶えています。」 最初の検知から約 24 時間後。これ以降は復帰まで沈黙します
  4. 復帰
    復帰をお知らせ — URL が再び叩かれたら「『ラベル』が復帰しました。」を 1 回送り、監視は通常状態に戻ります。次に途絶すれば、また 1/3 から数え直します。
push を見逃しても大丈夫 途絶している間は、アプリ内のその URL に「途絶」バッジが常時付きます。push は best-effort(到達保証なし)なので、確実に気づくための“真実”はこのアプリ内表示です。

※ ミュート中の URL では、途絶通知も届きません(アプリ内の「途絶」バッジだけが付きます)。

レスポンス

状況コードbody
受け付けた202{"ok":true}
URL が失効済み410{"error":"url revoked"}
URL が存在しない404{"error":"url not found"}

セキュリティ上の理由から、応答には配信の詳細(端末への到達結果やミュート状態など)を含めません。202 は「受け付けた」ことを意味します。

URL の管理

URL 詳細画面。「通知を受け取る」(ミュート)と「この URL を失効」の操作が並ぶ
各 URL の詳細画面で、ミュート(通知を受け取る)や失効ができます

複数の端末で受け取る(端末リンク)

iPhone と Android スマホなど、自分の複数のスマホで同じ URL の通知を受け取れます。アプリ内で端末どうしを QR コードでリンクすると、両方の端末の URL がひとつに合算され、以後はどちらの端末にも同じ通知が届きます(同期)。送信側の Webhook URL は変わりません。

リンクのしかた

  1. 端末 A(すでに使っている方)で「設定 → 端末をリンク → この端末のコードを表示」を開きます(QR が表示されます)。
  2. 端末 B(追加したい方)で「設定 → 端末をリンク → QR を読み取って参加」を開き、A の QR を読み取ります。
  3. A 側に「追加しますか?」と出るので 「追加する」を押すと統合完了。両方の端末で同じ URL の通知を受け取れます。
自分の端末どうし専用です 端末リンクは同一人物の複数端末を想定しています。リンクした端末はアカウントを共有し、どちらからでも URL の発行・失効ができます。失効はどちらの端末からでも両方に反映されます(合算後は 1 本の URL)。

端末を外す(紛失・売却時)

「設定 → 端末をリンク → リンクした端末」から、特定の端末を解除できます。解除した端末は、以後その URL の通知を受信できなくなり、アカウントの操作(一覧の閲覧・失効・発行)もできなくなります。端末を紛失・売却したときの遮断に使ってください。

URL 文字列そのものを変えたいときは「失効」 端末の解除は「その端末を外す」操作で、Webhook URL の文字列は変わりません。URL が漏れた疑いがあるときは、解除ではなく失効(旧 URL は即 410)し、新しい URL を発行し直してください。

※ 通知履歴は端末ごとに保存され、合算されません(各端末には、その端末で受け取った分だけが残ります)。アカウント全体の引き継ぎ(ログイン)は今後対応予定です。