If the destination of a webhook is publicly reachable, it is necessary to ensure that incoming requests come from the desired source system. A secret key can be used to ensure the origin of a webhook.

To create a new webhook, simply specify any URL. The secret key may also remain empty. However, if your target URL is publicly available, you should set a secret key and validate this value when processing the webhooks. Otherwise, anyone can send you appropriate requests to this webhook URL, which will be processed by you. Without the secret key, the target system has no reliable way to know if the event really comes from SeaTable.

If you specify a secret key, the webhook has an additional X-SeaTable-Signature-Header, which can be used to ensure the origin of the webhook. As long as someone else doesn’t know the secret key, they won’t be able to send you events via webhook and get the matching X-SeaTable-Signature-Header to generate.

For example, if a secret key is set, a webhook request looks like this:

-- HEADER --
Content-Type: application/json
Content-Length: 625
X-Seatable-Signature: sha256=598b55485e7875def064746867ff220e79d7c75512fdb931a39e313af5abfe60
Connection: keep-alive
Accept: */*
Accept-Encoding: gzip, deflate
User-Agent: python-requests/2.28.1
Host: example.com

-- CONTENT --
{
  event:update
  data: {
    dtable_uuid: 6c17f178ee724c109c68ecee364027fc
    row_id: Y_aYcE6wTo-IzGWb-oDmaQ
    op_user: 92d8f9b243f8437db0131c2536398403@auth.local
    op_type: create_row
    op_time: 1677595743.088
    table_id: 0000
    table_name: Table1
    row_name: ""
    row_data: []
  }
}

The secret key is a shared secret between SeaTable and the receiving system. SeaTable uses the secret key to calculate a hash value from the event. The receiving system can use the secret key to calculate the content back from the hash value and compare it to the actual content. If the actual content does not match the calculated content from the hash, the webhook should not be processed.

In every programming language there should be corresponding functions to be able to validate such a signature accordingly. How this check would look like in PHP, you will learn in this article .

For Python, a check might look like this:

import hmac
from flask import Flask, request

app = Flask(__name__)

@app.route('/receive-seatable-webhook', methods=['POST'])
def receive-seatable_webhook():
    secret = 'secret'
    seatable_signature = request.headers.get('X-Seatable-Signature', '').replace('sha256=', '')

    signature = hmac.new(
        secret.encode('utf-8'), request.data, digestmod='sha256').hexdigest()

    signature_compare = hmac.compare_digest(signature, seatable_signature)

    if signature_compare:
        # do something
        pass

    return {'success': signature_compare}