Free n8n template

Form submissions to Postgres n8n template

What it does

An importable n8n workflow that receives form webhooks, maps stable field IDs to columns, normalizes email, phone, dates and state codes, and upserts each submission into Postgres on its submission ID with a matching contact row. Claude classifies free-text answers, and a failed classification never blocks storage. Rejected rows land in a dead-letter table with a Slack alert. You need n8n, Postgres or Supabase, an Anthropic API key and Slack.

What this workflow does

  1. Form webhook answers the form provider immediately, so it never times out and resends.
  2. Validate and normalize (Code) reads Typeform, Tally, Jotform or plain JSON payloads, maps field refs (not question text) to columns, lowercases email, converts phone numbers to E.164, dates to ISO and states to two-letter codes, and turns multi-select answers into arrays. It also lists any field the mapping does not know.
  3. Valid? (If) sends rejected submissions to the dead-letter path with the reason.
  4. Has free text? (If) only calls the model when there is a real message to read.
  5. Classify with Claude (HTTP Request) returns a category, urgency and one-line summary from a fixed JSON schema at low effort.
  6. Attach classification (Code) handles refusals, truncated output and API errors by storing the row without tags.
  7. Upsert into Postgres writes the contact (matched on email) and the submission in one statement, keyed on the submission ID, so retries and replays update instead of duplicating.
  8. Notify team in Slack posts new or updated submissions and flags unmapped fields.
  9. Write to dead-letter table and Alert on failed submission keep the raw payload and reason for anything rejected or not stored.

Before you import

  • A Postgres or Supabase database. The SQL for the three tables is in the second sticky note.
  • A form tool that can send webhooks. Typeform and Tally send JSON; Jotform sends form data with a rawRequest field, which the template parses.
  • An Anthropic API key. The template uses claude-opus-5-5 at low effort; claude-haiku-5-5 is cheaper for high-volume, simple classification like this.
  • A Slack bot in the intake and exceptions channels.

Setup

  1. Run the SQL from the sticky note, then import the JSON and connect Postgres, Anthropic and Slack.
  2. Replace REPLACE_WITH_LONG_RANDOM_TOKEN in the webhook path with a long random string. Activate the workflow and paste the production URL into your form’s webhook settings.
  3. In Validate and normalize, replace each REPLACE_WITH_..._FIELD_REF with your field refs. Typeform lets you give each question a custom ref, which keeps mappings readable.
  4. Adjust REQUIRED and the classification categories to match your intake.
  5. Replace REPLACE_WITH_SLACK_INTAKE_CHANNEL_ID and REPLACE_WITH_SLACK_EXCEPTIONS_CHANNEL_ID.

Test it

Submit the form three times: once normally, once with a malformed phone number, and once more with the first submission’s exact payload resent from the provider’s webhook log. You should see one row in form_submissions, one row in form_submissions_failed, a Slack “New” message followed by an “Updated” message, and an exceptions alert. Then add a question to the form and confirm the Slack notice names the unmapped field.

Make it production-ready

  • Verify Typeform’s signature header, or another provider’s signing method, in addition to the secret path.
  • Copy uploaded files to your own storage, since provider file links can expire.
  • Schedule a nightly job that compares submission IDs from the form’s API with the database and replays anything missing.
  • Review the dead-letter table daily for the first month, and add an n8n error workflow.
  • Health, financial or identity data needs every tool in the path (form, n8n host, database) covered by the right agreements, including a BAA for HIPAA data.

Read the full guide on sending form submissions to a database for data modeling and reconciliation, or ask Lumeor to adapt it to your forms.

Next steps