> ## Documentation Index
> Fetch the complete documentation index at: https://help.pipelime.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Install methods: JavaScript snippet or direct endpoint

> The two ways to send submissions in, the platform guides, and the context fields each one accepts.

export const Screenshot = ({id, alt, caption, frame = 'browser', url, marks = [], lang = 'en', workspace = 'northwind-outbound'}) => {
  const src = `/images/screenshots/${lang}/${id}.png`;
  const chrome = url ? `app.pipelime.ai/${workspace}${url}` : 'app.pipelime.ai';
  return <figure className={`pl-shot pl-shot--${frame} not-prose`}>
      <div className="pl-shot__frame">
        {frame !== 'bare' && <div className="pl-shot__bar">
            <span className="pl-shot__dots">
              <span className="pl-shot__dot" />
              <span className="pl-shot__dot" />
              <span className="pl-shot__dot" />
            </span>
            <span className="pl-shot__url">{chrome}</span>
          </div>}
        <div className="pl-shot__media">
          <img src={src} alt={alt} loading="lazy" />
          {marks.map(mark => <span key={mark.n} className="pl-shot__mark" style={{
    left: `${mark.x}%`,
    top: `${mark.y}%`
  }} aria-hidden="true">
              {mark.n}
            </span>)}
        </div>
      </div>
      {caption && <figcaption className="pl-shot__caption">{caption}</figcaption>}
    </figure>;
};

Every form source takes submissions two ways: a JavaScript snippet that watches the forms already on your site, or a direct HTTP endpoint you post to yourself.

<div className="pl-availability">
  <div className="pl-availability__row">
    <div className="pl-availability__label">Where</div>
    <div className="pl-availability__value"><span className="pl-path">Web Forms<span className="pl-path__sep">→</span>Install</span></div>
  </div>

  <div className="pl-availability__row">
    <div className="pl-availability__label">Your role needs</div>
    <div className="pl-availability__value">Read access to Web form capture endpoint <code>read-web-forms</code>. Admin, Member and Viewer have it by default.</div>
  </div>

  <div className="pl-availability__row">
    <div className="pl-availability__label">To create</div>
    <div className="pl-availability__value"><code>create-web-forms</code> on top of the permission above.</div>
  </div>
</div>

You can reach this same screen from three places: the **Install** button on a form source's own page, the **Install** shortcut on its card in the [Web Forms](/en/finding-leads/web-forms-connect) list, and the last step of creating a source. On that last step, **Go to form source** closes the wizard and opens the source's own page, where the **Install** button brings this screen back.

## The two methods

| Method                 | Best for                                                            | What you do                                                                                       |
| ---------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| **JavaScript snippet** | Forms already live on your site, on any platform                    | Paste one script tag before `</body>`; every form on the page is found on its own                 |
| **Direct endpoint**    | Zapier, a Webflow logic flow, a WordPress hook, or code you control | POST each submission as JSON, matching the fields under [Direct endpoint](#direct-endpoint) below |

Whichever method you are setting up, the install screen keeps watching while it is open. Its footer names the domain it is listening on, and flips to **First form detected!** or **First submission received!** for the first form or submission that arrives while you are looking at it.

## JavaScript snippet

<Steps>
  <Step title="Copy the snippet">
    Use the copy button on the code block to copy the tag to your clipboard.
  </Step>

  <Step title="Paste it before the closing body tag">
    Add it just before `</body>` on every page that has a form.
  </Step>

  <Step title="Your forms are discovered automatically">
    The snippet detects every form on your pages and reports it. There is no per-form setup.
  </Step>
</Steps>

<Screenshot id="finding-leads/web-forms-install-methods--snippet-tab" url="/web-forms/12" alt="The install dialog's JavaScript snippet tab: three numbered steps above a copyable code block holding the script tag and this source's token" caption="Once this tag is live, a new form on the page doesn't need a second install. It's found the same way." />

What happens to a form once it's found, mapping its fields and turning tracking on, is covered on [Field mapping and the submissions inbox](/en/finding-leads/web-forms-mapping-and-submissions).

## Direct endpoint

Post each submission yourself, as JSON, to this source's own endpoint. One endpoint URL covers the whole source, carrying the source's token; `form_key` is what separates one form from another inside it.

| Field          | Rule            | What it does                                                                                                                                                                                                                                                                                                                   |
| -------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `fields`       | **Required**    | Your field names and values, up to 100 keys, one value per key (no nested objects or arrays). A key already named after a lead attribute (`email`, `name`, `first_name`, `last_name`, `phone`, `job_title`, `company_name`, `company_website`, `message`) maps on its own; anything else can be matched once in **Map fields** |
| `fields.email` | **Recommended** | Without an email value the submission waits in the review inbox instead of creating a lead on its own                                                                                                                                                                                                                          |
| `form_key`     | **Optional**    | Groups submissions under one form. A key you have not used before registers a form for you. Leave it out and posts land on the source's own default endpoint form                                                                                                                                                              |
| `context`      | **Optional**    | Page and campaign metadata, listed under [Context fields](#context-fields) below                                                                                                                                                                                                                                               |

<Note>
  A form the endpoint registers for you starts as **Discovered**, not **Tracking**, so those first
  submissions wait in the review inbox rather than becoming leads. Track the form once and the rest
  flow through.
</Note>

<Screenshot id="finding-leads/web-forms-install-methods--endpoint-tab" url="/web-forms/12" alt="The install dialog's Direct endpoint tab: a POST example built from this source's own endpoint URL and token, with the payload reference table underneath" caption="This workspace's real token is blurred here; every form source has its own." />

The full wire contract, every response code, the domain check, and how a duplicate or a honeypot hit is handled, is on [Web Forms: capture script and submissions endpoint](/en/developers/web-forms-capture-endpoint).

## Platform guides

<AccordionGroup>
  <Accordion title="Webflow (Site settings → Custom code)">
    Open your site's settings in Webflow, go to the Custom code tab, and paste the snippet into
    the Footer code box. Save and publish: custom code only runs on the published site, and it
    needs a paid Webflow site plan.
  </Accordion>

  <Accordion title="WordPress (WPCode plugin or theme footer)">
    Install a footer-injection plugin such as WPCode, or edit your theme's footer template, and
    paste the snippet in the Footer section so it loads on every page. Save, then clear your
    caching plugin so the change goes live. Works out of the box with Contact Form 7, WPForms,
    Gravity Forms and Elementor forms.
  </Accordion>

  <Accordion title="Shopify (Online Store → Themes → Edit code)">
    From your Shopify admin, go to Online Store → Themes, choose Edit code on your live theme,
    and open `layout/theme.liquid`. Paste the snippet just before the closing body tag and save:
    contact and newsletter forms are picked up on their own.
  </Accordion>

  <Accordion title="Framer (Site settings → Custom Code)">
    Open your project's Site settings in Framer, go to Custom Code, and paste the snippet into
    the End of body tag section. Publish your site.
  </Accordion>

  <Accordion title="Google Tag Manager (Custom HTML tag)">
    Create a new Custom HTML tag and paste the full snippet, including the script tag. Set the
    trigger to All Pages, then submit and publish the container.
  </Accordion>

  <Accordion title="Zapier (Webhooks by Zapier)">
    This one uses the direct endpoint, not the snippet. Add a Webhooks by Zapier action step to
    your Zap, choose the POST event, and paste the source's endpoint URL (the [Direct
    endpoint](#direct-endpoint) example above) into the URL field, with Payload Type set to
    `json`. Nest your form fields under a `fields` key, and include an email field.
  </Accordion>
</AccordionGroup>

<Screenshot id="finding-leads/web-forms-install-methods--platform-guides" url="/web-forms/12" alt="The collapsed Platform guides list inside the install dialog: Webflow, WordPress, Shopify, Framer, Google Tag Manager and Zapier, each with its own caption and a Snippet or Endpoint chip" caption="Every guide here uses the snippet except Zapier's, which is the one built on the direct endpoint." />

## Context fields

Every submission can carry the same four kinds of context, however it arrives:

| Category         | Fields                                                                                   | Comes from                                                                                                                                                  |
| ---------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Geolocation**  | Country, region, city                                                                    | Looked up from the submitter's IP once the submission lands, whichever method sent it. Nothing to send                                                      |
| **Device**       | IP address, user agent                                                                   | The request itself, whichever method sent it. Nothing to send                                                                                               |
| **Page context** | Page URL, page title, referrer                                                           | The page the snippet is running on. A direct integration has no page to read, so it sends these itself, inside `context`                                    |
| **Campaign**     | `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`, `gclid`, `fbclid` | The snippet reads them off the URL and keeps the first values it sees for the rest of the browser session. A direct integration sends them inside `context` |

`context` also takes `submitted_at`. The snippet stamps it at the moment of submit; a direct integration can set it itself, to timestamp a submission that didn't just happen.

## Related

<CardGroup cols={2}>
  <Card title="Connect the forms on your website" icon="globe" href="/en/finding-leads/web-forms-connect">
    Scan your site, map the fields, choose your settings, and reach this same install screen from the wizard's last step.
  </Card>

  <Card title="Field mapping and the submissions inbox" icon="inbox" href="/en/finding-leads/web-forms-mapping-and-submissions">
    Track or ignore a discovered form, map its fields, and work the submissions each method produces.
  </Card>

  <Card title="Web Forms: capture script and submissions endpoint" icon="webhook" href="/en/developers/web-forms-capture-endpoint">
    The endpoint's full wire contract: every response code, the domain check, and how duplicates and honeypot hits are handled.
  </Card>
</CardGroup>
