FormRill
  1. Blog

Send form submissions to Slack or Discord

Your team already lives in Slack or Discord, so new leads should land there too. Here is the no-server way to post every form submission to a channel today, why FormRill’s webhook can’t post to Slack or Discord on its own, and a small relay if you would rather write code.

By FormRill team3 min read

Native Slack and Discord notifications are coming soon. Until they ship, use one of the routes below. n8n and Make are a Pro feature, free during the beta ($9/month after launch). If your team uses Telegram, FormRill posts there natively on Free.

Why not point the webhook straight at Slack or Discord?

Slack and Discord both give you an incoming-webhook URL, so it is tempting to paste it into FormRill’s webhook automation. It won’t work. FormRill sends the same JSON shape to every webhook:

{
  "form_id": "f_…",
  "form_name": "Contact form",
  "submission_id": "s_…",
  "created_at": 1793750400000,
  "data": { "name": "Ada", "email": "ada@example.com", "message": "Hi!" }
}

Slack’s incoming webhooks need a text field and Discord’s need content (or an embed). This payload has neither, so both reject it. Your submission is still stored, and the failure shows in the delivery log, but no message appears. Something has to turn the payload into a chat message.

The no-server route: n8n or Make

Both tools have ready-made Slack and Discord steps, so the whole thing is configuration.

With n8n

  1. Create a workflow that starts with a Webhook node set to POST, and copy its production URL.
  2. In your form’s Automations tab in FormRill, add an n8n automation with that URL.
  3. Add n8n’s Slack or Discord node after the Webhook node and build the message from the incoming fields, for example form_name and data.email.
  4. Activate the workflow and send a test submission.

More on the setup in the n8n integration guide.

With Make

  1. Create a scenario that starts with a custom webhook, and copy its URL.
  2. In FormRill, add a Webhook automation with that URL and send one test submission so Make learns the fields.
  3. Add a Slack or Discord module and map the fields into the message.

Advanced: a small relay you deploy yourself

If you would rather not use an automation tool, a few lines of code can do the translation. This means running a function yourself, so it is the opposite of “no server”, but a Cloudflare Worker is free at this volume and takes a few minutes. Any serverless function works the same way.

1. Create the incoming webhook

Treat this URL like a password: anyone who has it can post to your channel.

2. Deploy the relay

Create a Worker (for example with npm create cloudflare@latest) and replace its code with:

// Receives FormRill's webhook and posts a message to Slack.
export default {
  async fetch(request, env) {
    const url = new URL(request.url)
    if (request.method !== 'POST' || url.searchParams.get('key') !== env.RELAY_KEY) {
      return new Response('Not found', { status: 404 })
    }

    const { form_name, data } = await request.json()
    const lines = Object.entries(data).map(([k, v]) => `*${k}:* ${v}`)
    const text = [`New submission on ${form_name}`, ...lines].join('\n')

    const res = await fetch(env.SLACK_WEBHOOK_URL, {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ text }),
    })
    // A non-2xx answer shows up as a failure in FormRill's delivery log.
    return new Response(null, { status: res.ok ? 204 : 502 })
  },
}

Store the two secrets, then deploy:

npx wrangler secret put SLACK_WEBHOOK_URL   # the URL from step 1
npx wrangler secret put RELAY_KEY           # any long random string
npx wrangler deploy

RELAY_KEY matters: FormRill’s webhooks aren’t signed yet, so a secret in the URL is what stops strangers posting to your channel through the relay.

For Discord instead: Discord uses content instead of text, **bold** instead of *bold*, and caps messages at 2,000 characters. Swap the middle of the Worker for this, and store the Discord URL as DISCORD_WEBHOOK_URL:

const lines = Object.entries(data).map(([k, v]) => `**${k}:** ${v}`)
const content = [`New submission on ${form_name}`, ...lines].join('\n').slice(0, 2000)

const res = await fetch(env.DISCORD_WEBHOOK_URL, {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ content }),
})

3. Point FormRill at it

In your form’s Automations tab, add a Webhook (Pro, free during the beta) with your Worker URL and the key:

https://form-relay.YOUR-SUBDOMAIN.workers.dev/?key=YOUR_RELAY_KEY

If a message doesn’t arrive

The submission is safe either way: FormRill stores it before delivering. Open the form’s Delivery log to see the status n8n, Make or your relay returned. You’ll get an email when a delivery fails, and FormRill retries it automatically for about a day. If you fix the cause sooner, press Retry to send it right away.

Get the webhook your channel needs

Free up to 500 submissions a month and unlimited forms. No server, no credit card.

Start free

Related