Send form submissions to your CRM and tools
Email tells a person; webhooks tell your systems
Notification emails are perfect for getting a submission in front of a human who acts on it. They are useless for getting a submission into a system. To create a CRM contact, open a support ticket, enrich a lead, post to a channel, or kick off a background job, you need the submission delivered as structured data, not prose in an inbox.
There are three ways to do that, in increasing order of flexibility and effort:
- Native integrations push submissions directly into a named destination (HubSpot, Slack, Google Sheets, and more) with no code.
- Automations route, enrich, and branch submissions through a no-code workflow before they leave the platform.
- Webhooks POST the full submission as JSON to any URL you control - the universal adapter for anything the first two cannot do.
All three are available on Pro plans and above (the forms product page shows the plan matrix). The rest of this guide covers each, plus the production details - signature verification, retries, idempotency - that separate a reliable integration from one that silently drops data.
Option 1: Native integrations (no code)
For common destinations, a native integration is the fastest path: you configure it once on the form and submissions flow through automatically, with no receiver to build or host.
The most common CRM destination is HubSpot. Formspring creates or updates a HubSpot CRM contact from each submission using a Private App access token; matching is by email, so repeat visitors update the same contact instead of creating duplicates. You configure the token, which submission field holds the email, and an optional JSON field map from your form keys to HubSpot's internal property names. The HubSpot integration doc has the step-by-step, including the scopes the Private App needs.
Beyond CRM, native integrations cover the destinations most teams reach for: Slack and Discord for instant team visibility, Google Sheets and Airtable for spreadsheet-driven workflows, Notion for docs, and Zapier, Make, and n8n as bridges to thousands of other apps. The integrations directory lists everything available. Use a native integration whenever one exists for your destination - it is less to build and less to maintain.
Option 2: Webhooks for anything custom
When no native integration fits - your own backend, an internal service, a destination nobody has built for - a webhook is the universal adapter. A webhook is a URL Formspring POSTs a JSON payload to whenever a clean submission lands. Webhooks are per-form, and a form can have several.
The payload carries the event type, the submission ID, the form ID, the full submitted fields, and a timestamp:
{
"type": "submission.created",
"submission_id": "01HFXX0X9R7KZJVN9VS6TG2C5T",
"form_id": "r2EdO-orF-3S",
"payload": { "email": "ada@example.com", "message": "Hi" },
"received_at": "2026-05-07T16:09:10Z"
}
To set one up: open your form, create a webhook with your endpoint URL, and copy the signing secret that is shown once at creation (store it somewhere safe - you will need it to verify deliveries). From then on, every clean submission that passes the spam stack is POSTed to your URL in the background. The webhooks overview walks the full lifecycle and the configuration doc covers the setup screen.
Verifying webhook deliveries
An unverified webhook endpoint is a security hole: anyone who learns the URL can POST fake submissions to it. The fix is to verify every delivery's signature before trusting it.
Every delivery is signed with an HMAC signature - specifically HMAC-SHA256 computed over the raw request body using the secret you copied at creation. The signature arrives in the X-Formspring-Signature header, alongside X-Formspring-Event and X-Formspring-Timestamp. In your handler, recompute the HMAC over the raw body with your stored secret and compare it to the header value in constant time (use a constant-time comparison function, never ==, to avoid timing attacks). If they do not match, reject the request.
Get the raw body before any JSON parsing or framework middleware reformats it - signing is over the exact bytes sent, so re-serialised JSON will not match. The signing & verification doc has the precise algorithm, and the blog post on verifying HMAC webhook signatures in Node, PHP, and Python has copy-paste verification snippets for each.
Retries, replay, and idempotency
Production webhook handling has three requirements that toy implementations skip - and skipping them is how submissions silently vanish.
Retries. Endpoints go down, deploy, and time out. Formspring retries failed deliveries automatically with exponential backoff, up to 7 attempts, so a brief outage does not lose a submission. The retries doc covers the backoff schedule and what counts as a failure (any non-2xx response).
Replay. Every delivery is logged with its status and response, and you can replay any delivery from the dashboard or the API at any time - essential when you fix a bug in your handler and need to reprocess the submissions it dropped. See the delivery log and replay doc, plus the post on why deliveries fail and how to replay them.
Idempotency. Because retries and replays exist, your handler will occasionally receive the same submission twice. It must tolerate that - key your processing on the submission_id so a duplicate delivery is a no-op rather than a duplicate CRM contact or a double-charged action. The post on webhook idempotency covers the pattern, and retry strategies explained covers the why behind the backoff.
Option 3: Automations - route and enrich
Between a one-click integration and a hand-built webhook receiver sits a third option: no-code automations. An automation is a per-form workflow - a trigger, an ordered list of steps, and a worker that runs them - that lets you route, enrich, and branch submissions without writing receiver code.
Automations support four step kinds: a condition (continue only when the payload matches a rule), a branch (choose one of two downstream paths), a delay (pause and resume later), and an action (send a conditional email, tag the submission, call an internal HTTP endpoint, classify with AI, or invoke an integration driver). That combination covers the routing logic teams usually reach for code to express: "if the message mentions billing, tag it sales and notify the revenue channel; otherwise post to support."
The difference from a raw webhook is that the logic lives in the platform, runs on a queue worker, stores each run's context, and is replayable - you get conditions, branching, and delays without standing up and maintaining a service. The triggers, conditions, and actions docs cover the building blocks, the examples doc has recipes, and the post on automations for routing and enrichment shows complete flows. Free plans get one active automation per form (100 runs/month); Pro and above are unlimited.
Which to use
Three tools, one decision:
- Native integration - when a built-in destination exists (HubSpot, Slack, Sheets, and the rest). Least to build, least to maintain. Start here.
- Automation - when you need routing logic (conditions, branching, delays, enrichment) but not a custom destination. No code, replayable, runs in the platform.
- Webhook - when you need to reach a system nobody has built an integration for, or you want full control in your own code. The universal adapter, but you own the receiver, the signature verification, and the idempotency.
The right architecture is often both an integration and a webhook: an integration for instant team visibility (a Slack ping) and a webhook for the system of record (your CRM or database). They are not mutually exclusive, and a single form can drive all three.
Webhooks, integrations, and unlimited automations all unlock on Pro and above. Start free to build the form, then upgrade when you are ready to wire it into your stack - the send-submissions-to-a-CRM use case for agencies shows what a full routing setup looks like in practice.