What this workflow does
- Form webhook answers the form provider immediately, so it never times out and resends.
- 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.
- Valid? (If) sends rejected submissions to the dead-letter path with the reason.
- Has free text? (If) only calls the model when there is a real message to read.
- Classify with Claude (HTTP Request) returns a category, urgency and one-line summary from a fixed JSON schema at low effort.
- Attach classification (Code) handles refusals, truncated output and API errors by storing the row without tags.
- 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.
- Notify team in Slack posts new or updated submissions and flags unmapped fields.
- 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
rawRequestfield, which the template parses. - An Anthropic API key. The template uses
claude-opus-5-5at low effort;claude-haiku-5-5is cheaper for high-volume, simple classification like this. - A Slack bot in the intake and exceptions channels.
Setup
- Run the SQL from the sticky note, then import the JSON and connect Postgres, Anthropic and Slack.
- Replace
REPLACE_WITH_LONG_RANDOM_TOKENin the webhook path with a long random string. Activate the workflow and paste the production URL into your form’s webhook settings. - In Validate and normalize, replace each
REPLACE_WITH_..._FIELD_REFwith your field refs. Typeform lets you give each question a customref, which keeps mappings readable. - Adjust
REQUIREDand the classification categories to match your intake. - Replace
REPLACE_WITH_SLACK_INTAKE_CHANNEL_IDandREPLACE_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.