機能
待ち時間アラート
場所のリアルタイムの待ち時間がしきい値を下回ると、Waity があなたの Webhook を呼び出します。エージェントが何度も問い合わせる必要はありません。Free を含むすべてのプランで使えます。
仕組み
1 つの場所について、しきい値(分)と https の Webhook を指定してアラートを作成します。毎分のデータ取り込みのたびに、Waity は新しい値が入ったばかりの場所を確認し、待ち時間がしきい値以下なら、署名付きの JSON 本文を Webhook に POST します。
アラートは GET /v1/places/{id} と同じリアルタイムの回答を読むため、API と食い違うことはありません。発火するのは、now.usable が true で、単位が分、now.value が under 以下のときだけです。下限値(「60 分超」)、古い値や一時停止中の値、閉まっている場所、データがない場合は発火しません。
アラートを作成
API キーと JSON 本文を付けて POST /v1/alerts を呼びます。レスポンスはアラートで、その id と署名用シークレットを含みます。シークレットは一度だけ表示されます:Webhook から読める場所に保存してください。
| フィールド | 指定する値 |
|---|---|
place_id | 1 つの場所、レーン、または部門。空港、国境の検問所、病院のキャンパスには単一の待ち時間がないため、その id はレーンや部門の id を添えて拒否されます。 |
under | 整数の分。1 から 600 まで。 |
webhook_url | Waity が POST する宛先。https のみです。下のルールを参照してください。 |
repeat | false(既定)は一度だけ発火し、その後アラートは fired になります。true は、待ち時間がしきい値より 5 分を超えて長くなった後に再び発火します。30 分に 1 回までです。 |
expires_at | 省略可、ISO 8601。既定は今から 24 時間後、最長 30 日です。 |
curl -X POST "https://api.waity.ai/v1/alerts" \
-H "X-Api-Key: $WAITY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"place_id":"pl_nvk0wkb3ps","under":30,"webhook_url":"https://example.com/waity/webhook"}'
201レスポンス
{
"id": "al_3k9qx2m7t4p8w1za",
"place": {
"id": "pl_nvk0wkb3ps",
"name": "Ysleta Passenger Vehicles, Standard",
"category": "border"
},
"under": 30,
"unit": "min",
"repeat": false,
"webhook_url": "https://example.com/waity/webhook",
"status": "armed",
"created_at": "2026-10-31T19:02:11Z",
"expires_at": "2026-11-01T19:02:11Z",
Webhook URL のルール
アラートがプライベートネットワークに届かないよう、Waity は次のような Webhook URL を拒否します:
- 既定のポート(443)で https を使っていない、またはユーザー名やパスワードを含む。
- ホスト名ではなく IP アドレスを使っている。そのため、プライベート、ループバック、リンクローカル、クラウドのメタデータのアドレスはすべて拒否されます。
- localhost、ラベルが 1 つだけの名前、または .local、.internal、.lan、.home.arpa のようなプライベートな名前や予約済みの名前を使っている。
- Waity 自体を指している。
Waity は自分では名前解決を行わず、リダイレクトにも従いません:3xx の応答は配信失敗として扱われます。
一覧、取得、削除
GET /v1/alerts はアラートを新しい順に、状態と最後の配信とともに返します。アカウントのキーは 1 つの一覧を共有し、アカウントのないキーは独自の一覧を持ちます。GET /v1/alerts/{id} は 1 件を取得し、DELETE /v1/alerts/{id} はそれを削除して配信を止めます。
curl
curl "https://api.waity.ai/v1/alerts" \
-H "X-Api-Key: $WAITY_API_KEY"
curl -X DELETE "https://api.waity.ai/v1/alerts/al_3k9qx2m7t4p8w1za" \
-H "X-Api-Key: $WAITY_API_KEY"
| 状態 | 指定する値 |
|---|---|
armed | 待ち時間がしきい値を下回るのを待っています。 |
fired | 発火済みの 1 回限りのアラートです。 |
expired | 発火しないまま expires_at を過ぎました。 |
failing | 最後の配信の試行が失敗しました。last_delivery を参照してください。残りがある間は再試行が続きます。 |
disabled | 配信の試行が 20 回続けて失敗しました。Webhook が動くようになったら、新しいアラートを作成してください。 |
Webhook
各配信は JSON 本文付きの POST です。まず warnings をユーザーに伝え、次に summary を伝えてください。now.value は now.usable_until まで有効です。それ以降は GET /v1/places/{id} にもう一度問い合わせてください。
POST
{
"type": "wait.under",
"alert": {
"id": "al_3k9qx2m7t4p8w1za",
"under": 30
},
"place": {
"id": "pl_nvk0wkb3ps",
"name": "Ysleta Passenger Vehicles, Standard",
"category": "border",
"url": "https://waity.ai/places/ysleta-passenger-vehicles-standard-kb3ps"
},
"now": {
"value": 25,
"unit": "min",
"measured_at": "2026-10-31T13:58:00-06:00",
"usable_until": "2026-10-31T15:28:00-06:00",
"basis": "estimate"
},
"summary": "About 25 min to enter the US, estimated by the border agency, updated 2 min ago.",
"warnings": [],
"links": {
"place": "https://api.waity.ai/v1/places/pl_nvk0wkb3ps",
"page": "https://waity.ai/places/ysleta-passenger-vehicles-standard-kb3ps",
"alert": "https://api.waity.ai/v1/alerts/al_3k9qx2m7t4p8w1za",
"docs": "https://waity.ai/docs/alerts"
}
}
各リクエストには次のヘッダーが付きます:
Waity-Signature: t=<unix>,v1=<hex>:tは署名した時刻(unix 秒)です。v1は、t、ドット、生の本文をつないだ文字列の HMAC-SHA256 で、アラートのシークレット全体をキーにします。Waity-Alert: アラートの id。Waity-Delivery: 配信の id。再試行のたびに同じなので、重複を捨てられます。
署名を検証する
JSON を解析する前に生のバイト列で署名を確認し、定数時間で比較し、5 分より古い t は拒否してください。
import crypto from "node:crypto";
import express from "express";
const app = express();
const secret = process.env.WAITY_ALERT_SECRET; // whsec_…
// The raw body: the signature covers the exact bytes Waity sent.
app.post("/waity/webhook", express.raw({ type: "application/json" }), (req, res) => {
const parts = Object.fromEntries(
(req.get("Waity-Signature") ?? "").split(",").map((p) => p.split("=", 2)),
);
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${req.body}`)
.digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) <= 300;
const ok =
fresh &&
typeof parts.v1 === "string" &&
parts.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
if (!ok) return res.status(400).end();
const event = JSON.parse(req.body);
// Read event.warnings first, then event.summary.
res.status(204).end();
});
app.listen(3000);
再試行
10 秒以内に任意の 2xx で応答してください。それ以外は失敗した試行となり、約 1, 5, 25 分後に再試行されます。20 回続けて失敗すると、アラートは無効になります。
プランごとの上限
各プランで同時に有効にできるアラートの数が決まっています。それを超えると 409 alert_limit が返り、プランの上限を示すメッセージが付きます。アラートへのリクエストは 1 回につき 1 回の呼び出しとして数え、配信は数えません。アカウントのないキーは Free として数えます。
| プラン | 有効なアラート |
|---|---|
| Free | 1 |
| Plus | 10 |
| Pro | 100 |
| Team | 1,000 |
ER と urgent care
ER と urgent care のアラートでは、すべての ER の回答と同じく、各配信の warnings[0] が 911 の案内で、一度だけ伝えます。
関連
この先 は、履歴から待ち時間がいつ下がりそうかを示します。回答の読み方 にすべてのフィールドの説明があり、すべてのフィールドは API リファレンス にあります。