Skip to content

About

Send every form submission to Slack - Formspree alternative with AI spam filtering. Verifies the webhook HMAC signature, posts a Block Kit message per submission.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

Repository files navigation

Slack contact form webhook �Formspree alternative with AI spam filtering

Receive SmartForm webhook events and forward every new submission to a Slack channel as a Block Kit message.

What your webhook receives

SmartForm POSTs a JSON event to your webhook URL on every submission. The body mirrors the same field structure as the public form endpoint:

Your submission fields �name, email, message, etc. exactly as the form sent them.

Reserved fields �names starting with _ are stripped before the webhook fires (they are control fields, not user data). The honeypot drop (_gotcha filled) means the webhook is not called �the submission is silently discarded.

Field In webhook payload? Notes
_gotcha No Drop trigger �never sent.
_next No UX control, stripped.
_subject No UX control, stripped.
submission_id Yes (added) Server-generated UUID for idempotency.
is_spam Yes (added) AI classification result.
intent Yes (added) sales / support / inquiry / spam (Pro).
timestamp Yes (added) Server time in ISO 8601.

Field names are Formspree-compatible �the SmartForm form endpoint and your webhook use the same conventions.

How it works

Browser  � SmartForm AI  � POST this Vercel function  � Slack incoming webhook
                                       â”?
                                       └─ verifies X-SmartForm-Signature
                                          before forwarding
  1. Create a form in https://usesmartform.com/dashboard.
  2. Set the form's Webhook URL to your deployed Vercel function URL (e.g. https://smartform-slack.vercel.app/api/webhook).
  3. SmartForm posts every new submission to that URL (signed with HMAC-SHA256).
  4. This function verifies the signature, builds a Slack Block Kit message, and forwards to Slack.

Setup

  1. Create a Slack incoming webhook: https://api.slack.com/messaging/webhooks �pick a channel �Copy URL.
  2. Clone, install, configure, deploy:
    git clone https://github.com/smartformai/smartform-example-webhook-slack.git
    cd smartform-example-webhook-slack
    npm install
    vercel link
    vercel env add SLACK_WEBHOOK_URL         # paste the URL from step 1
    vercel env add SMARTFORM_HMAC_SECRET     # from your SmartForm workspace settings
    vercel deploy --prod
  3. In SmartForm dashboard, set the form's Webhook URL to https://<your-deployment>.vercel.app/api/webhook.

The function

// api/webhook.ts �Vercel Edge Function
export const config = { runtime: 'edge' };

export default async function handler(req: Request) {
  if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 });
  const body = await req.text();

  if (!verifySignature(body, req.headers.get('X-SmartForm-Signature'))) {
    return new Response('Invalid signature', { status: 401 });
  }

  const event = JSON.parse(body);
  if (event.event !== 'submission.created') return new Response('ok', { status: 200 });

  const s = event.submission;
  const fields = Object.entries(s.data || {})
    .filter(([k]) => !k.startsWith('_'))
    .slice(0, 10)
    .map(([k, v]) => ({ type: 'mrkdwn', text: `*${k}*\n${String(v).slice(0, 800)}` }));

  const blocks = [
    { type: 'header', text: { type: 'plain_text',
        text: s.is_high_value ? '🔥 High-value lead' : (s.is_spam ? '🛡�Spam submission' : 'New form submission') } },
    { type: 'section', text: { type: 'mrkdwn', text: s.ai_summary || '_(no AI summary)_' } },
    ...(fields.length ? [{ type: 'section', fields }] : []),
    { type: 'context', elements: [
        { type: 'mrkdwn', text: `form=\`${event.form.form_id}\`  intent=\`${s.intent_label}\` (${s.intent_confidence.toFixed(2)})  spam=${s.spam_confidence.toFixed(2)}` }] },
  ];

  await fetch(process.env.SLACK_WEBHOOK_URL!, {
    method:  'POST',
    headers: { 'Content-Type': 'application/json' },
    body:    JSON.stringify({ blocks }),
  });

  return new Response('ok', { status: 200 });
}

verifySignature() is in lib/verify.ts �it recomputes HMAC-SHA256 with your shared secret and compares it to the X-SmartForm-Signature: sha256=<hex> header.

Webhook payload (from SmartForm)

{
  "event": "submission.created",
  "submitted_at": "2026-09-28T07:54:00Z",
  "form": { "id": "...", "form_id": "f_abc12345", "name": "Contact form" },
  "submission": {
    "id": "sub_01HXX...",
    "data": { "name": "Ada", "email": "[email protected]", "message": "Hi!" },
    "is_spam": false,
    "spam_confidence": 0.02,
    "intent_label": "sales",
    "intent_confidence": 0.93,
    "is_high_value": true,
    "ai_summary": "Visitor asks about pricing; mentions a 5-person team.",
    "ip_address": "203.0.113.5",
    "user_agent": "Mozilla/5.0 ..."
  }
}

Header: X-SmartForm-Signature: sha256=<hmac_hex>

Local test

npx vercel dev
curl -X POST http://localhost:3000/api/webhook \
  -H 'Content-Type: application/json' \
  -H 'X-SmartForm-Signature: sha256=<compute locally with your secret>' \
  -d @sample-payload.json

FAQ

Is there a free tier?

Yes. AI spam filtering is enabled by default on every plan. AI intent classification and high-value lead detection require a paid plan (Pro or Business) �the dashboard enforces this and returns HTTP 402 if you try to enable them on a free workspace.

Do I need an API key?

No. The form posts directly to a public endpoint using only an 8-char form ID, which is non-enumerable. The example also includes a hidden _gotcha honeypot field so naive bots cannot submit.

Does it verify the Slack signature?

No. This is a Slack-format sender (it POSTs to a Slack-compatible webhook after receiving a SmartForm event), not a Slack event receiver. For receiver-side verification, see the SmartForm docs.

Related examples

Discord webhook example | SmartForm JS SDK

License

MIT.

About

Send every form submission to Slack - Formspree alternative with AI spam filtering. Verifies the webhook HMAC signature, posts a Block Kit message per submission.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages