Updated at 2024-09-13 10:16

Revisions rev15

Atsuo Fukaya
JSON の post.name 部分の例示を改善
Updated by fukayatsu 2024-09-13 10:16:49 +0900
  • # Generic Webhookの概要
  • esaでは、記事やコメントの投稿・更新時に指定されたURLに対してHTTPリクエストを送ることができます。
  • # 設定方法
  • チームのOwnerが https://[your-team].esa.io/team/webhooks から各種Webhookを設定することができます
  • # リクエストの詳細
  • ## HTTPメソッド
  • `POST`
  • ## タイムアウト
  • 20秒(open timeout: 15秒)
  • ## リトライ
  • 1回
  • ※ リクエストがタイムアウトなどで失敗した際、同じリクエストが複数発行される場合があります。その場合は後述する `X-Esa-Delivery` ヘッダを確認することで同一のリクエストかどうかを判断することができます。
  • ## リクエストヘッダー
  • ### Content-Type
  • `application/json`
  • ### User-Agent
  • `esa-Hookshot/v1`
  • ### X-Esa-Delivery
  • 例: `1234`
  • リクエストに対する一意なIDです。
  • ### X-Esa-Signature
  • 後述するリクエストボディの値の改竄を防ぐためにSignatureを指定できます。Generic webhookの設定画面で `Secret (optinal)` を設定した場合にこのヘッダーが追加されます。
  • 例:
  • - Secretの値が`my_secret_key`
  • - Payloadの値が`{"kind":"post_create","post":{"name": "たいとる"}}`(実際は[Payloadの例](https://docs.esa.io/posts/37#json%20payload)のような値となります)
  • 上記の場合、esaのサーバはまず、このPayload(`{"kind":"post_create","post":{"name": "たいとる"}}`)に対して、SHA-256ハッシュ関数を利用し、`my_secret_key`を共有キーとした以下のようなHMACの値を生成します。
  • `5c2cd89667226539b34ea930781b8bf3e7f1236da7624eee6b748cd52899f91f`
  • esaのサーバは次に、Webhookで指定したURLに対して`HTTP_X_ESA_SIGNATURE`ヘッダに
  • HMACの値の先頭に`sha256=`という文字列を付与した以下のような文字列を設定し、リクエストを発行します。
  • `sha256=5c2cd89667226539b34ea930781b8bf3e7f1236da7624eee6b748cd52899f91f`
  • Webhookに設定したURLに対応するエンドポイントでは、`HTTP_X_ESA_SIGNATURE`ヘッダの値の`sha256=より後の値`を抜き出し、以下のようにHMACの値を生成し、Payloadに改ざんがないかどうかを確認します。
  • ```ruby:rackでHMACの値を生成し、ヘッダと比較する例
  • # 環境変数の SECRET_TOKEN には、my_secret_key という値が入っているとする
  • payload = request.body.read # {"kind":"post_create","post":{"name": "たいとる"}}
  • received_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload)
  • # このsignatureと
  • if Rack::Utils.secure_compare(received_signature, request.env['HTTP_X_ESA_SIGNATURE'])
  • # good
  • else
  • # ng
  • end
  • ```
  • ```bash:コマンドラインでHMACの値を生成する例
  • echo -n '{"kind":"post_create","post":{"name": "たいとる"}}' \
  • | openssl dgst -sha256 -hmac "my_secret_key"
  • ```
  • see also: [ppworks/rack-esa_webhooks](https://github.com/ppworks/rack-esa_webhooks)
  • ## リクエストボディ
  • json 形式で payload が渡されます。
  • ### 記事作成時(kind: "post_create")
  • ```json
  • {
  • "kind": "post_create",
  • "team": {
  • "name": "esa"
  • },
  • "post": {
  • "name": "たいとる",
  • "name": "foo/bar/たいとる #tag1 #tag2",
  • "body_md": "ほんぶん",
  • "body_html": "<p>ほんぶん</p>\n",
  • "message": "Create post.",
  • "wip": false,
  • "number": 1253,
  • "url": "https://example.esa.io/posts/1253"
  • },
  • "user": {
  • "icon": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
  • "thumb_s": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_ms": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_m": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_l": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
  • }
  • },
  • "name": "Atsuo Fukaya",
  • "screen_name": "fukayatsu"
  • }
  • }
  • ```
  • ### 記事更新時(kind: "post_update")
  • ```json
  • {
  • "kind": "post_update",
  • "team": {
  • "name": "esa"
  • },
  • "post": {
  • "name": "たいとる",
  • "name": "foo/bar/たいとる #tag1 #tag2",
  • "body_md": "ほんぶん",
  • "body_html": "<p>ほんぶん</p>\n",
  • "message": "Update post.",
  • "wip": false,
  • "number": 1253,
  • "url": "https://example.esa.io/posts/1253",
  • "diff_url": "https://example.esa.io/posts/1253/revisions/3"
  • },
  • "user": {
  • "icon": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
  • "thumb_s": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_ms": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_m": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_l": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
  • }
  • },
  • "name": "Atsuo Fukaya",
  • "screen_name": "fukayatsu"
  • }
  • }
  • ```
  • ### 記事archive時(kind: "post_archive")
  • ```json
  • {
  • "kind": "post_archive",
  • "team": {
  • "name": "esa"
  • },
  • "post": {
  • "name": "Archived/たいとる",
  • "name": "Archived/foo/bar/たいとる #tag1 #tag2",
  • "body_md": "ほんぶん",
  • "body_html": "<p>ほんぶん</p>\n",
  • "message": "Archived!",
  • "wip": false,
  • "number": 1253,
  • "url": "https://example.esa.io/posts/1253"
  • },
  • "user": {
  • "icon": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
  • "thumb_s": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_ms": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_m": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_l": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
  • }
  • },
  • "name": "Atsuo Fukaya",
  • "screen_name": "fukayatsu"
  • }
  • }
  • ```
  • ### 記事削除時(kind: "post_delete")
  • ```json
  • {
  • "kind": "post_delete",
  • "team": {
  • "name": "esa"
  • },
  • "post": {
  • "name": "たいとる",
  • "name": "foo/bar/たいとる #tag1 #tag2",
  • "wip": false,
  • "number": 1253
  • },
  • "user": {
  • "icon": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
  • "thumb_s": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_ms": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_m": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_l": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
  • }
  • },
  • "name": "Atsuo Fukaya",
  • "screen_name": "fukayatsu"
  • }
  • }
  • ```
  • ### 外部公開の開始時(kind: "post_start_sharing")
  • ```json
  • {
  • "kind": "post_start_sharing",
  • "team": {
  • "name": "esa"
  • },
  • "post": {
  • "name": "たいとる",
  • "name": "foo/bar/たいとる #tag1 #tag2",
  • "body_md": "ほんぶん",
  • "body_html": "<p>ほんぶん</p>\n",
  • "message": "Create post.",
  • "wip": false,
  • "number": 1253,
  • "url": "https://example.esa.io/posts/1253"
  • },
  • "user": {
  • "icon": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
  • "thumb_s": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_ms": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_m": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_l": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
  • }
  • },
  • "name": "Atsuo Fukaya",
  • "screen_name": "fukayatsu"
  • },
  • "sharing": {
  • "url": "https://esa-pages.io/p/sharing/105/posts/37/587eedb862cde79c9a79.html"
  • }
  • }
  • ```
  • ### 外部公開の停止時(kind: "post_stop_sharing")
  • ```json
  • {
  • "kind": "post_stop_sharing",
  • "team": {
  • "name": "esa"
  • },
  • "post": {
  • "name": "たいとる",
  • "name": "foo/bar/たいとる #tag1 #tag2",
  • "body_md": "ほんぶん",
  • "body_html": "<p>ほんぶん</p>\n",
  • "message": "Create post.",
  • "wip": false,
  • "number": 1253,
  • "url": "https://example.esa.io/posts/1253"
  • },
  • "user": {
  • "icon": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
  • "thumb_s": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_ms": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_m": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_l": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
  • }
  • },
  • "name": "Atsuo Fukaya",
  • "screen_name": "fukayatsu"
  • }
  • }
  • ```
  • ### コメント作成時(kind: "comment_create")
  • ```json
  • {
  • "kind": "comment_create",
  • "team": {
  • "name": "esa"
  • },
  • "post": {
  • "name": "Archived/たいとる",
  • "name": "foo/bar/たいとる #tag1 #tag2",
  • "body_md": "ほんぶん",
  • "body_html": "<p>ほんぶん</p>\n",
  • "message": "Update post.",
  • "wip": false,
  • "number": 1253,
  • "url": "https://example.esa.io/posts/1253#comment-6385"
  • },
  • "comment": {
  • "body_md": "こめんと",
  • "body_html": "<p>こめんと</p>\n"
  • },
  • "user": {
  • "icon": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
  • "thumb_s": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_ms": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_m": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_l": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
  • }
  • },
  • "name": "Atsuo Fukaya",
  • "screen_name": "fukayatsu"
  • }
  • }
  • ```
  • ### メンバー追加時(kind: "member_join")
  • ```json
  • {
  • "kind": "member_join",
  • "team": {
  • "name": "esa"
  • },
  • "user": {
  • "icon": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
  • "thumb_s": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_ms": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_m": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
  • },
  • "thumb_l": {
  • "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
  • }
  • },
  • "name": "Atsuo Fukaya",
  • "screen_name": "fukayatsu"
  • }
  • }
  • ```

Generic Webhookの概要

esaでは、記事やコメントの投稿・更新時に指定されたURLに対してHTTPリクエストを送ることができます。

設定方法

チームのOwnerが https://[your-team].esa.io/team/webhooks から各種Webhookを設定することができます

リクエストの詳細

HTTPメソッド

POST

タイムアウト

20秒(open timeout: 15秒)

リトライ

1回

※ リクエストがタイムアウトなどで失敗した際、同じリクエストが複数発行される場合があります。その場合は後述する X-Esa-Delivery ヘッダを確認することで同一のリクエストかどうかを判断することができます。

リクエストヘッダー

Content-Type

application/json

User-Agent

esa-Hookshot/v1

X-Esa-Delivery

例: 1234

リクエストに対する一意なIDです。

X-Esa-Signature

後述するリクエストボディの値の改竄を防ぐためにSignatureを指定できます。Generic webhookの設定画面で Secret (optinal) を設定した場合にこのヘッダーが追加されます。

例:

  • Secretの値がmy_secret_key
  • Payloadの値が{"kind":"post_create","post":{"name": "たいとる"}}(実際はPayloadの例のような値となります)

上記の場合、esaのサーバはまず、このPayload({"kind":"post_create","post":{"name": "たいとる"}})に対して、SHA-256ハッシュ関数を利用し、my_secret_keyを共有キーとした以下のようなHMACの値を生成します。

5c2cd89667226539b34ea930781b8bf3e7f1236da7624eee6b748cd52899f91f

esaのサーバは次に、Webhookで指定したURLに対してHTTP_X_ESA_SIGNATUREヘッダに
HMACの値の先頭にsha256=という文字列を付与した以下のような文字列を設定し、リクエストを発行します。

sha256=5c2cd89667226539b34ea930781b8bf3e7f1236da7624eee6b748cd52899f91f

Webhookに設定したURLに対応するエンドポイントでは、HTTP_X_ESA_SIGNATUREヘッダの値のsha256=より後の値を抜き出し、以下のようにHMACの値を生成し、Payloadに改ざんがないかどうかを確認します。

rackでHMACの値を生成し、ヘッダと比較する例
# 環境変数の SECRET_TOKEN には、my_secret_key という値が入っているとする
payload = request.body.read # {"kind":"post_create","post":{"name": "たいとる"}}
received_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload)

# このsignatureと
if Rack::Utils.secure_compare(received_signature, request.env['HTTP_X_ESA_SIGNATURE'])
  # good
else
  # ng
end
コマンドラインでHMACの値を生成する例
echo -n '{"kind":"post_create","post":{"name": "たいとる"}}' \
| openssl dgst -sha256 -hmac "my_secret_key"
コマンドラインでHMACの値を生成する例
echo -n '{"kind":"post_create","post":{"name": "たいとる"}}' \
| openssl dgst -sha256 -hmac "my_secret_key"

see also: ppworks/rack-esa_webhooks

リクエストボディ

json 形式で payload が渡されます。

記事作成時(kind: "post_create")

json
{
    "kind": "post_create",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "たいとる",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Create post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
json
{
    "kind": "post_create",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Create post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}

記事更新時(kind: "post_update")

json
{
    "kind": "post_update",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "たいとる",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Update post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253",
        "diff_url": "https://example.esa.io/posts/1253/revisions/3"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
json
{
    "kind": "post_update",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Update post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253",
        "diff_url": "https://example.esa.io/posts/1253/revisions/3"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}

記事archive時(kind: "post_archive")

json
{
    "kind": "post_archive",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "Archived/たいとる",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Archived!",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
json
{
    "kind": "post_archive",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "Archived/foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Archived!",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}

記事削除時(kind: "post_delete")

json
{
    "kind": "post_delete",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "たいとる",
        "wip": false,
        "number": 1253
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
json
{
    "kind": "post_delete",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "wip": false,
        "number": 1253
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}

外部公開の開始時(kind: "post_start_sharing")

json
{
    "kind": "post_start_sharing",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "たいとる",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Create post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    },
    "sharing": {
        "url": "https://esa-pages.io/p/sharing/105/posts/37/587eedb862cde79c9a79.html"
    }
}
json
{
    "kind": "post_start_sharing",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Create post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    },
    "sharing": {
        "url": "https://esa-pages.io/p/sharing/105/posts/37/587eedb862cde79c9a79.html"
    }
}

外部公開の停止時(kind: "post_stop_sharing")

json
{
    "kind": "post_stop_sharing",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "たいとる",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Create post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
json
{
    "kind": "post_stop_sharing",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Create post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}

コメント作成時(kind: "comment_create")

json
{
    "kind": "comment_create",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "Archived/たいとる",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Update post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253#comment-6385"
    },
    "comment": {
        "body_md": "こめんと",
        "body_html": "<p>こめんと</p>\n"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
json
{
    "kind": "comment_create",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Update post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253#comment-6385"
    },
    "comment": {
        "body_md": "こめんと",
        "body_html": "<p>こめんと</p>\n"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}

メンバー追加時(kind: "member_join")

json
{
    "kind": "member_join",
    "team": {
        "name": "esa"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
# Generic Webhookの概要

esaでは、記事やコメントの投稿・更新時に指定されたURLに対してHTTPリクエストを送ることができます。

# 設定方法
チームのOwnerが https://[your-team].esa.io/team/webhooks から各種Webhookを設定することができます

# リクエストの詳細
## HTTPメソッド
`POST`

## タイムアウト
20秒(open timeout: 15秒)

## リトライ
1回

※ リクエストがタイムアウトなどで失敗した際、同じリクエストが複数発行される場合があります。その場合は後述する `X-Esa-Delivery` ヘッダを確認することで同一のリクエストかどうかを判断することができます。

## リクエストヘッダー
### Content-Type
`application/json`

### User-Agent
`esa-Hookshot/v1`

### X-Esa-Delivery
例: `1234`

リクエストに対する一意なIDです。


### X-Esa-Signature

後述するリクエストボディの値の改竄を防ぐためにSignatureを指定できます。Generic webhookの設定画面で `Secret (optinal)` を設定した場合にこのヘッダーが追加されます。

例: 

- Secretの値が`my_secret_key`
- Payloadの値が`{"kind":"post_create","post":{"name": "たいとる"}}`(実際は[Payloadの例](https://docs.esa.io/posts/37#json%20payload)のような値となります)

上記の場合、esaのサーバはまず、このPayload(`{"kind":"post_create","post":{"name": "たいとる"}}`)に対して、SHA-256ハッシュ関数を利用し、`my_secret_key`を共有キーとした以下のようなHMACの値を生成します。

`5c2cd89667226539b34ea930781b8bf3e7f1236da7624eee6b748cd52899f91f`

esaのサーバは次に、Webhookで指定したURLに対して`HTTP_X_ESA_SIGNATURE`ヘッダに
HMACの値の先頭に`sha256=`という文字列を付与した以下のような文字列を設定し、リクエストを発行します。

`sha256=5c2cd89667226539b34ea930781b8bf3e7f1236da7624eee6b748cd52899f91f`

Webhookに設定したURLに対応するエンドポイントでは、`HTTP_X_ESA_SIGNATURE`ヘッダの値の`sha256=より後の値`を抜き出し、以下のようにHMACの値を生成し、Payloadに改ざんがないかどうかを確認します。

```ruby:rackでHMACの値を生成し、ヘッダと比較する例
# 環境変数の SECRET_TOKEN には、my_secret_key という値が入っているとする
payload = request.body.read # {"kind":"post_create","post":{"name": "たいとる"}}
received_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload)

# このsignatureと
if Rack::Utils.secure_compare(received_signature, request.env['HTTP_X_ESA_SIGNATURE'])
  # good
else
  # ng
end
```

```bash:コマンドラインでHMACの値を生成する例
echo -n '{"kind":"post_create","post":{"name": "たいとる"}}' \
| openssl dgst -sha256 -hmac "my_secret_key"
```

see also: [ppworks/rack-esa_webhooks](https://github.com/ppworks/rack-esa_webhooks)

## リクエストボディ

json 形式で payload が渡されます。

### 記事作成時(kind: "post_create")

```json
{
    "kind": "post_create",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Create post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
```

### 記事更新時(kind: "post_update")

```json
{
    "kind": "post_update",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Update post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253",
        "diff_url": "https://example.esa.io/posts/1253/revisions/3"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
```

### 記事archive時(kind: "post_archive")
```json
{
    "kind": "post_archive",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "Archived/foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Archived!",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
```

### 記事削除時(kind: "post_delete")

```json
{
    "kind": "post_delete",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "wip": false,
        "number": 1253
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
```

### 外部公開の開始時(kind: "post_start_sharing")

```json
{
    "kind": "post_start_sharing",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Create post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    },
    "sharing": {
        "url": "https://esa-pages.io/p/sharing/105/posts/37/587eedb862cde79c9a79.html"
    }
}
```

### 外部公開の停止時(kind: "post_stop_sharing")

```json
{
    "kind": "post_stop_sharing",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Create post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
```

### コメント作成時(kind: "comment_create")

```json
{
    "kind": "comment_create",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Update post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253#comment-6385"
    },
    "comment": {
        "body_md": "こめんと",
        "body_html": "<p>こめんと</p>\n"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
```

### メンバー追加時(kind: "member_join")

```json
{
    "kind": "member_join",
    "team": {
        "name": "esa"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}
```

Generic Webhookの概要

esaでは、記事やコメントの投稿・更新時に指定されたURLに対してHTTPリクエストを送ることができます。

設定方法

チームのOwnerが https://[your-team].esa.io/team/webhooks から各種Webhookを設定することができます

リクエストの詳細

HTTPメソッド

POST

タイムアウト

20秒(open timeout: 15秒)

リトライ

1回

※ リクエストがタイムアウトなどで失敗した際、同じリクエストが複数発行される場合があります。その場合は後述する X-Esa-Delivery ヘッダを確認することで同一のリクエストかどうかを判断することができます。

リクエストヘッダー

Content-Type

application/json

User-Agent

esa-Hookshot/v1

X-Esa-Delivery

例: 1234

リクエストに対する一意なIDです。

X-Esa-Signature

後述するリクエストボディの値の改竄を防ぐためにSignatureを指定できます。Generic webhookの設定画面で Secret (optinal) を設定した場合にこのヘッダーが追加されます。

例:

  • Secretの値がmy_secret_key
  • Payloadの値が{"kind":"post_create","post":{"name": "たいとる"}}(実際はPayloadの例のような値となります)

上記の場合、esaのサーバはまず、このPayload({"kind":"post_create","post":{"name": "たいとる"}})に対して、SHA-256ハッシュ関数を利用し、my_secret_keyを共有キーとした以下のようなHMACの値を生成します。

5c2cd89667226539b34ea930781b8bf3e7f1236da7624eee6b748cd52899f91f

esaのサーバは次に、Webhookで指定したURLに対してHTTP_X_ESA_SIGNATUREヘッダに
HMACの値の先頭にsha256=という文字列を付与した以下のような文字列を設定し、リクエストを発行します。

sha256=5c2cd89667226539b34ea930781b8bf3e7f1236da7624eee6b748cd52899f91f

Webhookに設定したURLに対応するエンドポイントでは、HTTP_X_ESA_SIGNATUREヘッダの値のsha256=より後の値を抜き出し、以下のようにHMACの値を生成し、Payloadに改ざんがないかどうかを確認します。

rackでHMACの値を生成し、ヘッダと比較する例
# 環境変数の SECRET_TOKEN には、my_secret_key という値が入っているとする
payload = request.body.read # {"kind":"post_create","post":{"name": "たいとる"}}
received_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload)

# このsignatureと
if Rack::Utils.secure_compare(received_signature, request.env['HTTP_X_ESA_SIGNATURE'])
  # good
else
  # ng
end
コマンドラインでHMACの値を生成する例
echo -n '{"kind":"post_create","post":{"name": "たいとる"}}' \
| openssl dgst -sha256 -hmac "my_secret_key"

see also: ppworks/rack-esa_webhooks

リクエストボディ

json 形式で payload が渡されます。

記事作成時(kind: "post_create")

json
{
    "kind": "post_create",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Create post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}

記事更新時(kind: "post_update")

json
{
    "kind": "post_update",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Update post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253",
        "diff_url": "https://example.esa.io/posts/1253/revisions/3"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}

記事archive時(kind: "post_archive")

json
{
    "kind": "post_archive",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "Archived/foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Archived!",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}

記事削除時(kind: "post_delete")

json
{
    "kind": "post_delete",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "wip": false,
        "number": 1253
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}

外部公開の開始時(kind: "post_start_sharing")

json
{
    "kind": "post_start_sharing",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Create post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    },
    "sharing": {
        "url": "https://esa-pages.io/p/sharing/105/posts/37/587eedb862cde79c9a79.html"
    }
}

外部公開の停止時(kind: "post_stop_sharing")

json
{
    "kind": "post_stop_sharing",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Create post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}

コメント作成時(kind: "comment_create")

json
{
    "kind": "comment_create",
    "team": {
        "name": "esa"
    },
    "post": {
        "name": "foo/bar/たいとる #tag1 #tag2",
        "body_md": "ほんぶん",
        "body_html": "<p>ほんぶん</p>\n",
        "message": "Update post.",
        "wip": false,
        "number": 1253,
        "url": "https://example.esa.io/posts/1253#comment-6385"
    },
    "comment": {
        "body_md": "こめんと",
        "body_html": "<p>こめんと</p>\n"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}

メンバー追加時(kind: "member_join")

json
{
    "kind": "member_join",
    "team": {
        "name": "esa"
    },
    "user": {
        "icon": {
            "url": "https://img.esa.io/uploads/production/users/1/icon/402685a258cf2a33c1d6c13a89adec92.png",
            "thumb_s": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_s_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_ms": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_ms_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_m": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png"
            },
            "thumb_l": {
                "url": "https://img.esa.io/uploads/production/users/1/icon/thumb_l_402685a258cf2a33c1d6c13a89adec92.png"
            }
        },
        "name": "Atsuo Fukaya",
        "screen_name": "fukayatsu"
    }
}