# Custom Webhooks Integration Guide

Complete guide to integrate MyFormConnect with Custom Webhooks

      Setup time: **15-30 minutes
      Difficulty: **Intermediate
        Compatible versions: **Any HTTPS endpoint**

Receive form submission data at your own HTTP endpoint in real time. MyFormConnect sends a POST request to your server whenever a qualifying event fires — you control what happens next. Use this for custom CRM integrations, database writes, notifications, or any bespoke business logic.

## ** Prerequisites

- ** A MyFormConnect account — [sign up here](/users/sign_up)
- ** A publicly accessible HTTPS endpoint that accepts `POST application/json`
- ** Developer Options enabled on your MyFormConnect team — go to **Developer Options → Webhooks**
- ** Basic familiarity with HTTP and JSON

## ** Integration Methods

### Register Endpoint

                5 minutes

                Easy

Register your server URL with MyFormConnect and choose which events trigger the webhook.

                  1

#### Deploy a public HTTPS endpoint

                    Your endpoint must be reachable from the internet and must accept `POST` requests with a `Content-Type: application/json` body. It must return a `2xx` HTTP response to acknowledge receipt.

1. Deploy your endpoint on any platform — Node.js, Python, Ruby, PHP, Go, or a serverless function (AWS Lambda, Vercel Function, Cloudflare Worker, etc.)
2. Confirm the URL uses `https://` — HTTP endpoints are not accepted
3. Make sure the endpoint responds within ~10 seconds; return `200 OK` immediately and process work asynchronously if needed
4. Test locally with a tool like [ngrok](https://ngrok.com) before deploying to production

##### Important Note

                            MyFormConnect retries failed deliveries up to 5 times with exponential back-off (15 seconds, 1 minute, 5 minutes, 15 minutes, 1 hour). If your endpoint is slow or returns a non-2xx status, you will receive retries.

                  2

#### Add the endpoint in MyFormConnect

                    Register your endpoint URL and name it so you can identify it later.

1. In your MyFormConnect dashboard, go to **Developer Options → Webhooks**
2. Click **Add New Endpoint**
3. Enter a **Name** — e.g. *My CRM handler*
4. Paste your endpoint URL in the **URL** field
5. Click **Save

##### Important Note

                            Keep endpoint URLs private. Anyone with the URL can POST data to your server — validate the payload on your side.

                  3

#### Select event types

                    Choose which events will trigger your endpoint.

1. Open the endpoint you just saved
2. In the **Event Types** field, select one or both:
3. **form_response.created** — fires on every submission, irrespective of spam status
4. **lead.created** — fires only after spam detection, when a Lead record is created (clean submission with name + email)
5. Click **Save

##### Important Note

                            Subscribing to both event types will send two separate webhook calls per qualifying submission — one when the response is stored, and one when the lead is created. Only do this if you need both.

                  4

#### Submit a test and inspect delivery

                    Trigger a real submission to verify your endpoint receives data correctly.

1. Submit a test entry through your form on your website
2. In MyFormConnect, go to **Developer Options → Webhooks → [your endpoint]** and check **Delivery Attempts**
3. A successful delivery shows HTTP `200` and the request/response body
4. A failed delivery shows the error code — check your server logs to debug

### Sample Payloads

                Reference only

                Easy

Both examples below show a **Contact Us form** submission (name, email, message fields). The envelope structure is the same for all events; only `event_type` and `data` differ. All IDs are obfuscated strings (not raw database integers).

                  1

#### form_response.created — Contact Us form

                    Fired immediately when the form is submitted. Includes the raw form fields inside `data.submitted_data`. Fires **irrespective of spam status** — spam submissions are included. Honeypot and CAPTCHA fields are omitted.

```
{
  "event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "event_type": "form_response.created",
  "data": {
    "id": "PYvAJe",
    "form_id": "oJwWkJ",
    "name": "Jane Doe",
    "email": "jane@example.com",
    "company_name": null,
    "company_domain": null,
    "submitted_data": {
      "name": "Jane Doe",
      "email": "jane@example.com",
      "message": "I'd like to learn more about your services."
    },
    "created_at": "2026-06-13T10:30:00.000Z",
    "updated_at": "2026-06-13T10:30:00.000Z"
  }
}
```

##### Important Note

                            Keys inside `data.submitted_data` match the `name` attributes of your HTML form inputs. If your form has a field `<input name="company">`, it appears as `submitted_data.company`.

                  2

#### lead.created — Contact Us form

                    Fired only after spam detection passes and a Lead record is created. Requires the submission to contain both a name and an email address. The raw message body is **not** present on the lead object.

```
{
  "event_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "event_type": "lead.created",
  "data": {
    "id": "qelXVe",
    "team_id": "bJYLew",
    "email": "jane@example.com",
    "phone_number": null,
    "source": "Website",
    "status": "New",
    "assigned_to": null,
    "converted_at": null,
    "first_contacted_at": null,
    "last_activity_at": null,
    "name": "Jane Doe",
    "industry": null,
    "location": null,
    "notes": null,
    "created_at": "2026-06-13T10:30:05.000Z",
    "updated_at": "2026-06-13T10:30:05.000Z"
  }
}
```

##### Important Note

                            The message body a visitor typed is *not* on the lead object. If you need the message, also subscribe to `form_response.created` and correlate by email/timestamp, or read it directly from the MyFormConnect dashboard.

### Handler Examples

                15-20 minutes

                Intermediate

Minimal server-side handler examples. Both respond `200` immediately and log the payload — replace the log statement with your actual business logic.

                  1

#### Node.js (Express)

                    A minimal Express endpoint that parses the JSON envelope and reads form fields.

```
const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhooks/mfc', (req, res) => {
  // Acknowledge receipt immediately
  res.status(200).json({ received: true });

  const { event_type, event_id, data } = req.body;

  if (event_type === 'form_response.created') {
    const { email, name, submitted_data } = data;
    const message = submitted_data?.message ?? '';
    console.log(`New submission from ${name} <${email}>: ${message}`);
    // TODO: insert into your database, forward to CRM, etc.
  }

  if (event_type === 'lead.created') {
    const { email, name, phone_number, status } = data;
    console.log(`New lead: ${name} <${email}> — status: ${status}`);
    // TODO: create contact in CRM
  }
});

app.listen(3000, () => console.log('Webhook server listening on port 3000'));
```

##### Important Note

                            Always call `res.status(200).json(...)` *before* doing any slow work. If your handler times out before responding, MyFormConnect treats the delivery as failed and retries.

                  2

#### Python (Flask)

                    A minimal Flask endpoint doing the same thing.

```
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhooks/mfc', methods=['POST'])
def mfc_webhook():
    payload = request.get_json()
    event_type = payload.get('event_type')
    data = payload.get('data', {})

    if event_type == 'form_response.created':
        name = data.get('name')
        email = data.get('email')
        message = data.get('submitted_data', {}).get('message', '')
        print(f'New submission from {name} <{email}>: {message}')
        # TODO: your logic here

    elif event_type == 'lead.created':
        name = data.get('name')
        email = data.get('email')
        print(f'New lead: {name} <{email}>')
        # TODO: your logic here

    return jsonify({'received': True}), 200

if __name__ == '__main__':
    app.run(port=3000)
```

##### Important Note

                            For production use, run Flask behind a WSGI server (Gunicorn, uWSGI) and deploy behind a reverse proxy (Nginx, Caddy). The dev server is not suitable for production.

                  3

#### Idempotency tip

                    Each webhook delivery includes a unique `event_id`. Store processed event IDs to avoid handling the same event twice if retries occur.

1. On receipt, check if `event_id` already exists in your processed-events log
2. If it does, return `200` immediately (acknowledge but skip processing)
3. If it does not, process the event and persist the `event_id`
4. This prevents duplicate CRM entries or double emails if MyFormConnect retries a delivery

##### Important Note

                            MyFormConnect retries up to 5 times with exponential back-off. Idempotency handling is especially important for operations like sending notification emails or charging customers.

## ** Troubleshooting

              Check that the endpoint is saved with the correct URL and at least one event type selected. Confirm a form submission was made — MyFormConnect only sends webhooks when events actually occur. Also check that the form's domain is in the allowed domains list.

              Your endpoint is not reachable. Verify it is deployed to a public server with a valid HTTPS certificate. Test the URL directly with `curl -X POST https://yoursite.com/webhooks/mfc -H 'Content-Type: application/json' -d '{}'` and check for errors.

              Confirm your handler reads `req.body` (or equivalent) and that the JSON body-parser middleware is set up. Log the full raw payload first to verify the structure matches what you expect.

              MyFormConnect creates a Lead only when the submission passes spam detection and contains both a name and an email address. If your test submission was missing either field or was flagged as spam, no lead.created event fires. Switch to form_response.created temporarily to confirm the raw submission arrived.

              If your endpoint occasionally returns a non-2xx status or times out, MyFormConnect retries the delivery. Implement idempotency using the `event_id` field to deduplicate — see the Handler Examples tab.

#### Still having issues?

Can't find the answer you're looking for? Our support team is here to help with your integration.

          [** Contact Support](/support)

## ** Related Integrations

      [Zapier Integration Guide →](/docs/tools/code/zapier)      [Make.com Integration Guide →](/docs/tools/code/make)      [Slack Integration Guide →](/docs/tools/code/slack)

## ** References & Official Documentation

- ** [Webhook Security Best Practices](https://webhooks.fyi/) **
- ** [MDN Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) **
- ** [Express.js Documentation](https://expressjs.com/en/guide/routing.html) **
- ** [Flask Documentation](https://flask.palletsprojects.com/) **

## ** Next Steps

#### ** Configure MyFormConnect

- ** Set up your timezone
- ** Configure lead notifications
- ** Set up advanced spam detection
- ** Add team members to your account

#### ** Advanced Features

- ** Use our custom form templates
- ** Try AI Insights for Leads
- ** Integrate with your CRM
- ** Set up automated workflows
