Agent skill
SeaTable Webhooks Skill
Receive and verify SeaTable webhooks. Use when setting up SeaTable webhook handlers, debugging X-Seatable-Signature verification, or handling base change events (insert_row, modify_row, delete_row).
Install this skill
npx skills add hookdeck/webhook-skills --skill seatable-webhooks
When to Use This Skill
- How do I receive SeaTable webhooks?
- How do I verify the
X-Seatable-Signatureheader? - Why is my SeaTable webhook signature verification failing?
- How do I handle
insert_row,modify_rowanddelete_rowchanges? - Why did SeaTable mark my webhook as invalid?
Verification (core)
SeaTable signs the raw request body with HMAC-SHA256, using the webhook's secret key as a UTF-8 string. The header is X-Seatable-Signature: sha256=<hex>.
The secret key is optional. If none is set on the webhook, SeaTable sends no signature header, so anyone who finds the URL can post to it. Always set a secret.
const crypto = require('crypto');
function verify(rawBody, signatureHeader, secret) {
if (!signatureHeader || !secret) return false;
const received = signatureHeader.replace(/^sha256=/, '').toLowerCase();
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
try {
return crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
} catch {
return false; // length mismatch = invalid
}
}
Read the body raw for any content type (express.raw({ type: '*/*' })) and parse JSON only after verification.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Common Event Types
Every delivery has event (currently always update) and data. What changed is in data.op_type:
data.op_type | Triggered When |
|---|---|
insert_row | A row is added or duplicated (UI, form or API) |
modify_row | A row's content changes; data.row_data holds column_name, value, old_value |
delete_row | A row is deleted |
SeaTable's docs list insert_row as the value, but one sample on the secret-key page shows create_row. The examples accept both. Structural changes (new columns, views, comments, row locks) send nothing. A multi-row paste in the web editor sends one webhook with an extra data.row_count.
Environment Variables
SEATABLE_WEBHOOK_SECRET=your_webhook_secret_here # secret key set on the webhook
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 seatable --path /webhooks/seatable
Reference Materials
- references/overview.md - Webhook concepts, payload structure, op_types
- references/setup.md - Creating a webhook and secret key in SeaTable
- references/verification.md - Signature details and gotchas