LinearはElideの連携カタログに2026年5月22日にLiveとして追加されました。最初にリリースしたイベントはbroken_link_hookです。スキャナーがダウンした短縮リンクを発見すると、接続時に選択したチームにLinear issueを作成し、ボディにクリックメトリクスを含め、タグに基づいたラベルルーティングを行います。この記事はエンジニア向けのウォークスルーです。認証の仕組み、JSONペイロードの内容、そして同じパイプラインをクリック閾値スパイクに拡張してオンコール担当者が深夜3時のアラートではなくチケットを受け取れるようにした方法を解説します。
本番環境で数百から数千の短縮リンクを管理している場合、この障害パターンはご存知でしょう。マーケティングがキャンペーン先を変更し、新しいURLが404を返しても、顧客がBlueSkyでリンク切れのスクリーンショットを投稿するまで誰も気づきません。Linearはチームが既にバグのトリアージを行う場所なので、チケットもそこに置くのが自然です。
Personal API KeyでLinearを接続する
Linear連携はOAuthではなくPersonal API Keyを使用します。この選択には3つの理由があります。API KeyはワークスペースにスコープされているからでOAuthトークンが特定のユーザーに紐づくよりも管理者の交代を乗り越えやすく、LinearのAPI: Authenticationドキュメントもサーバー間のジョブには明示的にこれを推奨しているからです。
Linearでキーを生成してください。Settings > API > Personal API keys > Create key の順に進みます。後で迷わず取り消せるようelido-integrationという名前をつけてください。キー(先頭がlin_api_)をコピーして、ElideダッシュボードのLinear連携カードに貼り付けます。
その後の流れ: viewerクエリでキーを検証し、次にteamsクエリでチームピッカーを構成します。デフォルトチームを選択すると、Linearが割り当てたチームIDを含む行がintegration_configsテーブル(Postgres)に書き込まれます。複数のチームがある場合は、同じ画面でタグベースのルーティングを追加できます。詳しくは後述します。
POST /v1/workspaces/:id/integrations/linear/connect
{
"api_key": "lin_api_<redacted>",
"default_team_id": "TEAM_a1b2c3",
"default_priority": 2,
"labels": ["short-link", "auto-filed"]
}
バックエンドでは、api-coreサービスがADR-0036のエンベロープ暗号化スキームを使ってキーを暗号化して保存します。復号化されたキーは実際のGraphQL呼び出し中のみメモリに存在します。生の値はログに記録されず、連携ログUIには末尾4文字のみが表示されます。
注意点: LinearのPersonal API Keyは作成したユーザーに紐づきます。そのユーザーが退職し、Linearのシートを無効化すると、キーも無効になります。ベストプラクティスとして、Linearにサービス用ユーザー(例: [email protected])を作成し、そのアカウントからキーを生成することをお勧めします。
broken_link_hookイベント - 何が発火し、ボディには何が含まれるか
url-scannerサービスはワークスペース内のすべてのアクティブな短縮リンクを週次でクロールします。各リンクに対して、宛先にHTTP HEADリクエストを送り、HEADが非対応の場合はGETを実行し、TLSチェーンを検証します。4つの条件でリンク切れ状態になります。
- 2回連続のプローブで HTTP 4xxまたは5xx (一時的な500を吸収するための二重チェック)
- 先週まで有効だった TLSが期限切れまたは自己署名
- DNS NXDOMAIN - 宛先ホストが解決できなくなった
- パークドドメインのフィンガープリントマッチ - 宛先は解決するが、レスポンスボディが既知のスクワッターテンプレートと一致する(小さなフィンガープリントセットを管理)
これら4つのいずれかが発生すると、スキャナーはlink.brokenイベントをRedpandaにパブリッシュします。webhook-dispatcherがこれを消費し、アクティブな連携を検索して、Linearに対して以下のペイロードを生成します。
ステージング環境で実際に取得したbroken_link_hookペイロードです(一部フィールドは省略):
{
"event": "link.broken",
"link_id": "01J9V7QXMZ8K2Y3N4P5R6T7W8Z",
"short_url": "https://s.elido.me/spring-launch",
"destination_url": "https://oldcampaign.example.com/landing",
"failure_type": "http_5xx",
"failure_detail": "502 Bad Gateway, 2 consecutive probes",
"last_working_at": "2026-05-28T14:22:00Z",
"detected_at": "2026-06-04T03:11:42Z",
"clicks_last_7d": 2841,
"clicks_last_24h": 412,
"top_referrers": [
{ "host": "linkedin.com", "clicks": 1203 },
{ "host": "twitter.com", "clicks": 488 },
{ "host": "direct", "clicks": 612 }
],
"tags": ["campaign-spring-2026", "paid"],
"owner_email": "[email protected]"
}
services/api-core/internal/integrations/linear/broken_link_hook.goのLinearアダプターはそのペイロードを受け取り、LinearのGraphQL Issues APIに対するミューテーションを構築します。issueのタイトルはgrepで検索しやすい固定パターンに従います。
[Elido] Broken link: /spring-launch (502 Bad Gateway)
ボディは5つのセクションで構成される構造化Markdownです: リンク詳細、最後に動作していたタイムスタンプ、7日間ベースラインとのクリックデルタ、上位3リファラー、提案される修正ブロック。修正ブロックはfailure_typeを確認して事前定義の提案を選択します。http_5xxの場合は「宛先がレート制限中かデプロイ中か確認してください」、parked_domainの場合は「ドメインが期限切れまたはスクワットされている可能性があります。このリンクをアーカイブしてください」などです。
ラベルは2つのソースから割り当てられます: 接続時に設定したデフォルトラベルセットと、タグリストから動的に生成されるラベルです。タグがpaidやorganicに一致する場合、PMがLinearビューをフィルタリングできるようラベルとして追加します。
デデュプリケーション、レート制限、デッドレターキュー
broken_link_hookイベントは宛先ホストごとに24時間デデュプリケーションします。oldcampaign.example.comがダウンし、800の短縮リンクがそこを指している場合、800件の別々のチケットではなく、ボディに800件の短縮URLをまとめた1件のLinearチケットを受け取ります。これは初期ベータからの痛い教訓です。ダウンしたドメインに最初に遭遇した顧客は埋もれてしまいました。
LinearのGraphQLエンドポイントにはワークスペースごとのグローバルレート制限があります。webhook-dispatcherはRetry-Afterヘッダーを追跡し、フルジッターを伴う指数バックオフを最大5回試みます。5回後、イベントはデッドレターキューに格納されます。DLQエントリはSettings > Integrations > Linear > Failed eventsで確認でき、ワンクリックで再実行できます。DLQはプログラムによる再実行のためwebhooks機能経由でも利用可能です。
クリック閾値とカスタムトリガー
同じLinearアダプターがclick_threshold_hookイベントも処理します。Elideダッシュボードでリンクまたはキャンペーンごとに閾値を設定すると、リンクがバンドを超えた時にLinear issueを作成します。現在2種類のバンドタイプをサポートしています。
- スパイク: 直近1時間のクリック数が過去7日間の時間別ベースラインのN倍を超えた場合(デフォルトNは3)。バイラル拡散の検出や、あまり嬉しくないケースではボットトラフィックの検出に役立ちます。
- クリフ: 直近1時間のクリック数がベースラインの10%未満に落ち込んだ場合。死んだキャンペーンの検出に役立ちます。上流で有料広告が一時停止されると、マーケティングスタンドアップ前にLinearチケットとして確認できます。
click_threshold_hookのペイロード例:
{
"event": "link.click_threshold",
"link_id": "01J9V7QXMZ8K2Y3N4P5R6T7W8Z",
"short_url": "https://s.elido.me/spring-launch",
"band": "spike",
"current_hour_clicks": 8421,
"baseline_hourly_clicks": 612,
"multiplier": 13.76,
"top_referrers": [
{ "host": "news.ycombinator.com", "clicks": 6203 },
{ "host": "direct", "clicks": 1488 }
],
"tags": ["campaign-spring-2026"],
"triggered_at": "2026-06-04T11:14:00Z"
}
スパイクの場合、修正ブロックには「オーガニックトラフィックであることを確認し、リファラースプーフィングキャンペーンでないか確認してください。上記のリファラー内訳を確認してください」と表示されます。クリフの場合は「上流でキャンペーンがまだライブかどうか確認してください。一時停止されていた場合はこのリンクをアーカイブしてください」と表示されます。
複数チームへのタグベースルーティング
デフォルトのチームピッカーは20人規模のワークスペースには十分です。大規模な組織では、マーケティングリンクのLinearチケットはMarketingチームに、docsリンクのチケットはDocumentationチームに送りたいでしょう。タグベースルーティングがこれを処理します。
ルーティングルールはintegration_configs.routing_jsonに保存され、上から順番に評価されます。ルールの例:
[
{
"tag_glob": "campaign-*",
"team_id": "TEAM_growth",
"labels": ["growth", "urgent"]
},
{ "tag_glob": "docs-*", "team_id": "TEAM_docs", "labels": ["docs"] },
{
"tag_glob": "internal-*",
"team_id": "TEAM_internal",
"labels": ["internal"]
},
{ "default": true, "team_id": "TEAM_a1b2c3" }
]
リンク上の少なくとも1つのタグにglobが一致した最初のルールが適用されます。何も一致しない場合はデフォルトルールがイベントを処理します。globの構文はLinearのsaved-viewフィルターと同じなので、PMはすでに知っています。
failure_typeでルーティングすることもできます。TLS障害はテナントのカスタムドメインの証明書設定ミスを示すことが多いため、全てのTLS障害をプラットフォームチームに送りたいチームもあります。failure_type: tls_expiredをキーにしたルールを追加するだけで完了です。
webhookによるカスタムトリガー
公開しているすべてのイベントタイプでLinearチケットを作成したいわけではないチームもあります。完全なイベントカタログはwebhooks機能ページに記載されていますが、Linearと並行してよく設定される組み合わせは次のとおりです。
- 新規リンク監査のためのLinearチームへの
link.created(まれ、通常はコンプライアンスチーム向け) - カスタムドメインでのTLS異常のための
domain.takeover_detected - 週次サマリーチケットのための
link.scan_complete(1回のスキャン実行につき1つのissueで、フラグされた全リンクを一覧表示)
必要なイベントがカタログにない場合は、汎用webhookターゲットとobservabilityガイドを使ってカスタムのものを構築できます。または、当社の公開LinearボードにFeature Requestを投稿してください。メタですが再帰的です。
料金と各プランで利用できる機能
Linear連携はProティア以上に含まれています。Freeプランでは、Linearを接続できますがbroken_link_hookのみ利用可能です(クリック閾値やカスタムトリガーは非対応)。完全な対応表は料金ページをご確認ください。コンプライアンス上の理由でこの機能を検討している大規模チームの方 - 例えばGDPR第32条がスクワッタードメインを指す壊れたリダイレクトからのデータ漏洩検出を要求している場合 - は、Enterpriseソリューションページをご覧ください。
関連記事
- リンクイベントのwebhook: 開発者ガイド - Linearを含む全ての連携を動かす基盤となるイベントバス。
- 12のGoサービスにSentryを接続する - LinearイベントをトリガーするDispatcherを監視する方法。Dispatcher自体の障害を検知するために。
- リンクロット防止戦略 - リンク切れ検出を構築した背景にある、より広い運用上の経緯。
連携カタログ全体は2026年6月時点で43のベンダーを掲載しており、LinearはLiveの20件のうちの1つです。JiraをLinearの代わりに使用しているチームは、そのアダプターがベータ版で提供されています。メールでご連絡いただければ有効化いたします。
よくある質問
ElideはLinearとどのように認証を行いますか?
OAuthではなく、ワークスペースにスコープされたPersonal API Keyを使用します。LinearのSettings > APIでキーを生成し、Elideの連携カードに貼り付けてください。キーは当社のvaultの外に出ることはなく、ログからは隠蔽されます。キーをローテーションした場合、次のイベント発生時にサイレントエラーではなくソフトな再認証プロンプトが表示されます。
broken_link_hookイベントで「壊れたリンク」と判定される条件は何ですか?
url-scannerは週次で全てのアクティブな短縮リンクをクロールし、4つの条件をフラグします: 2回連続のプローブでHTTP 4xxまたは5xx、TLS証明書の期限切れまたは検証不能、DNS NXDOMAIN、そして既知のパークドドメインのフィンガープリントマッチ。これら4つのいずれかが発生すると、単一のLinear issueが作成されます。同じ宛先ホストに対して24時間デデュプリケーションされるため、1つのダウンドメインで800件のチケットが作成されることはありません。
リンクのタグに基づいて異なるLinearチームにissueを送ることはできますか?
はい。接続画面でデフォルトチームを選択し、ルーティングルールを追加できます。例えば、campaign-*に一致するタグはGrowthチームに、docs-*に一致するタグはEngineeringチームに送ります。ルールは上から順番に評価され、デフォルトのフォールバックがあります。ルールセットはPostgresに保存されるため、管理者トレールで変更を監査できます。
壊れたリンクだけでなく、クリック閾値アラートにも対応していますか?
はい、Phase 12から対応しています。同じLinearアダプターがbroken_link_hookと並んでclick_threshold_hookイベントも処理します。Elideダッシュボードでリンクまたはキャンペーンごとに閾値を設定すると、リンクがバンドを超えた時 - スパイク(1時間でベースラインの3倍)またはクリフ(ベースラインの10%未満に急落) - にLinear issueを作成します。
Linearが連携にレート制限をかけた場合はどうなりますか?
LinearのGraphQLエンドポイントはRetry-Afterヘッダー付きの429を返します。webhook-dispatcherはこれを尊重し、最大5回まで指数バックオフを行い、その後イベントをデッドレターキューに格納します。DLQのエントリはSettings > Integrations > Linear > Failed eventsで確認でき、ワンクリックで再実行できます。DLQはGraphQL API(/v1/integrations/linear/dlq)経由でも利用可能です。本番環境でLinearから持続的な429を受けたことはまだありません。
Elidoを試す
URLを貼り付けて短縮リンクを取得
登録不要。リンクは30日間有効。永久に保存するには登録してください。
Free、登録不要 · 1日あたり2件