A submit button's **Form Type** setting controls how form data leaves the browser. This page lists the three options and exactly how each one packages and delivers the data — useful both when you configure the button and when you hand off the details to whoever writes the endpoint.

## Overview

| Form Type | HTTP request? | Files allowed? | Content-Type | Typical use |
|---|---|---|---|---|
| **JSON** *(default)* | Yes — POST | Yes (base64) | `application/json` | Standard JSON APIs. |
| **Multipart/Form-Data** | Yes — POST | Yes (binary) | `multipart/form-data` | File-storage endpoints and legacy APIs. |
| **Open view with form data** | No | No | n/a | Multi-step flows and filter panels inside the app. |

Pick one, then set the **Submit URL** (required for **JSON** and **Multipart/Form-Data**; ignored for **Open view with form data**).

---

## JSON

**Use when:** the endpoint expects a JSON body.

**How the request reaches the backend:**

- `POST` request to the **Submit URL**.
- Header: `Content-Type: application/json`.
- Body: one JSON object with two top-level keys, `data` and `meta`.
- Files are base64-encoded and embedded inside `data`.

**Body shape:**

```json
{
  "data": {
    "username": "john",
    "email": "john@example.com",
    "profilePhoto": {
      "filename": "photo.jpg",
      "mimetype": "image/jpeg",
      "data": "base64encodedcontent..."
    },
    "attachments": [
      { "filename": "doc1.pdf", "mimetype": "application/pdf", "data": "..." },
      { "filename": "doc2.pdf", "mimetype": "application/pdf", "data": "..." }
    ]
  },
  "meta": {
    "button_id": "btn-1",
    "company_id": 6789,
    "event_id": "abc123",
    "interface_view_id": "view-1",
    "resource": "deal",
    "resource_id": 456,
    "user_id": 12345
  }
}
```

- `data` — every input in the form, keyed by its internal field name.
- A single-file input is one object. A multi-file input — one with **Allow multiple files** turned on — is always an array, including when the person picked one file, so a receiving service never branches on the file count. See [File Fields](#file-fields).
- `meta` — where the submission came from (which view, which button, which Pipedrive resource, which user). `company_id`, `resource_id` and `user_id` are numbers; the rest are strings. `company_id` and `user_id` are added server-side from the authenticated session and overwrite any client-supplied values.
- Any **Query parameters** you add to the submit button are appended to the URL.

**Body shape from a [floating window button](/docs/buttons/interface-button/interface-builder/explanation/floating-window-buttons):** that run has no Pipedrive record, so `resource` and `resource_id` are sent as `null`. Both keys stay in place — the payload shape is the same on both kinds of button, and every other `meta` value is unchanged:

```json
{
  "data": {
    "username": "john",
    "email": "john@example.com"
  },
  "meta": {
    "button_id": "btn-1",
    "company_id": 6789,
    "event_id": "abc123",
    "interface_view_id": "view-1",
    "resource": null,
    "resource_id": null,
    "user_id": 12345
  }
}
```

The values are never faked: a missing record is `null`, never `0` and never `""`.

**With `Omit oneclick metadata` on:** the body is just the inner `data` object — no `meta`, no outer wrapping. Use this for endpoints that reject unknown fields (for example Pipedrive API v2):

```json
{
  "username": "john",
  "email": "john@example.com",
  "profilePhoto": { "filename": "photo.jpg", "mimetype": "image/jpeg", "data": "..." }
}
```

**After submission:** both **Select view** and **Wait for response** are available.

---

## Multipart/Form-Data

**Use when:** the endpoint expects a multipart upload — file-storage APIs, legacy endpoints that cannot parse JSON, or anything that wants files as raw binary instead of base64.

**How the request reaches the backend:**

- `POST` request to the **Submit URL**.
- Header: `Content-Type: multipart/form-data` (the browser adds the correct boundary automatically).
- Every form field is a separate part. The part name starts with `data_` for form inputs and `meta_` for context.

**Parts:**

```
meta_button_id: "btn-1"
meta_company_id: "6789"
meta_event_id: "abc123"
meta_interface_view_id: "view-1"
meta_resource: "deal"
meta_resource_id: "456"
meta_user_id: "12345"

data_username: "john"
data_email: "john@example.com"
data_profilePhoto: (binary, filename="photo.jpg")
data_attachments[0]: (binary, filename="doc1.pdf")
data_attachments[1]: (binary, filename="doc2.pdf")
```

- `data_*` — form input values, keyed by the input's internal field name.
- `meta_*` — the same context as the `meta` object in JSON mode (`company_id` and `user_id` are added server-side from the authenticated session). Multipart is a text transport, so every value arrives as a string.
- Single-file inputs land at `data_<field>`. Multi-file inputs — those with **Allow multiple files** turned on — are always indexed: `data_<field>[0]`, `data_<field>[1]`, and so on, including when the person picked one file. See [File Fields](#file-fields).

**Parts from a [floating window button](/docs/buttons/interface-button/interface-builder/explanation/floating-window-buttons):** multipart form data cannot carry `null`, and the string `"null"` would be a lie. So instead of a placeholder value, the two record parts are **not sent at all** — five `meta_*` parts arrive instead of seven:

```
meta_button_id: "btn-1"
meta_company_id: "6789"
meta_event_id: "abc123"
meta_interface_view_id: "view-1"
meta_user_id: "12345"

data_username: "john"
data_email: "john@example.com"
```

Read the absence of the part as "there is no record". Nothing is sent as `0` or `""`.

**With `Omit oneclick metadata` on:** the `meta_*` parts are not sent and the `data_` prefix on form inputs is dropped — every input goes through under its bare name. The `Content-Type` is still `multipart/form-data`:

```
username: "john"
email: "john@example.com"
profilePhoto: (binary, filename="photo.jpg")
attachments[0]: (binary, filename="doc1.pdf")
attachments[1]: (binary, filename="doc2.pdf")
```

**After submission:** both **Select view** and **Wait for response** are available.

---

## File Fields

**Read this before you write the endpoint.** What decides whether a file field arrives as an array is the input's **Allow multiple files** setting — never how many files the person happened to pick.

| The input | JSON | Multipart/Form-Data |
|---|---|---|
| **Allow multiple files** off | One object under `<field>` | One part named `data_<field>` |
| **Allow multiple files** on | Always an array under `<field>` | Always indexed parts `data_<field>[0]`, `data_<field>[1]`, … |

The two shapes below come from the *same* submission: a form with a single-file input named `profilePhoto` and a multi-file input named `attachments`, where the person picked **one file into each**.

```json
{
  "data": {
    "profilePhoto": {
      "filename": "photo.jpg",
      "mimetype": "image/jpeg",
      "data": "base64encodedcontent..."
    },
    "attachments": [
      { "filename": "doc1.pdf", "mimetype": "application/pdf", "data": "..." }
    ]
  }
}
```

```
data_profilePhoto: (binary, filename="photo.jpg")
data_attachments[0]: (binary, filename="doc1.pdf")
```

`attachments` is an array of one, and stays an array when the person picks five. So the endpoint reads a multi-file field with one code path and never counts first.

:::caution[An empty multi-file selection is not an empty array]
A multi-file input the person never touched — or emptied again — arrives as the **empty string**, exactly as an empty single-file input always has. It never arrives as `[]`. The field is still present, so testing for the key is not enough — check the value before you iterate.

```json
{ "data": { "attachments": "" } }
```

```
data_attachments: ""
```

In multipart the field arrives as an ordinary text part with an empty value: no indexed parts, and no `filename`.
:::

A form built before **Allow multiple files** existed forwards exactly the payload it always did — the setting is off by default, and an input with it off is untouched by this rule.

**One exception — the redirect URL, not the payload.** When **After submission** is **Select view**, every selected file also becomes a URL parameter carrying its *file name*, and those parameters are indexed for both kinds of input: `attachments[0]=doc1.pdf`. That was true before this rule existed and has not changed, so a view already reading `attachments[0]` keeps working.

---

## File Size Limits

A submission that carries files is weighed against a byte budget before anything is forwarded. The budget belongs to the **Form Type**:

| Form Type | Budget for the files of one submission |
|---|---|
| **JSON** | 10 MB |
| **Multipart/Form-Data** | 100 MB |

- The budget is **one cap per submission, across every file field together**. Two file inputs in the same form share it, and so do the several files of one multi-file input. It is not a per-file limit and not a per-field limit.
- A submission over budget gets HTTP `413` back and is never forwarded to the **Submit URL**. The message names both the budget and the size the submission carried, and the user sees it as an error toast.
- A **Multipart/Form-Data** submission over 100 MB is stopped while the request is still being read, before the budget is ever weighed. It fails, but not with the `413` above, and the error the user sees is not the clear message this section describes. 100 MB is the size limit on the whole request as well as the multipart budget.
- MB means 1 048 576 bytes, the same unit the file input's own size display uses.
- JSON gets the smaller budget because it embeds each file as base64 — the payload the server has to build in memory ends up several times the size of the files themselves. Multipart holds the files in memory too, but roughly once and without the base64 inflation, so it can afford the larger number. A form that needs to move more than 10 MB belongs on **Multipart/Form-Data**.
- A file input's own **Max File Size**, **Max Total Size** and **Max Number of Files** settings are separate, browser-side checks. They can only narrow what a person is allowed to pick; none of them raises the budget above the numbers in the table, and the server puts no cap on the file count at all.

---

## Open view with form data

**Use when:** the submit should not call an API at all, but instead jump the user to another view and carry the form values along as URL parameters. Useful for multi-step flows, search forms, and filter panels inside the interface.

**How it works:**

- **No HTTP request is made.** Any **Submit URL** is ignored.
- The user is navigated to the view picked in the **View ID** field of the **After submission** setting.
- Every form input value is appended as a URL search parameter on that navigation.

**Restrictions:**

- The **File** input type is not allowed — the submission cannot pass binary data through a URL.
- **After submission** must be **Select view** (since there is no server to wait for).

**How values end up in the URL:**

- Each input's internal name becomes the URL key, and the input's value becomes the URL value. Form input values are **always** included on the navigation — there is no opt-out at runtime for this form type.
- Any explicit **Parameters** you set on the submit button are merged on top.
- The **Use input values as search params** toggle on the submit button does not affect runtime URL building — input values are forwarded regardless. The toggle is a *design-time* hint that adds the form's inputs to the **Expected Parameters from Views** list in the target view's URL Configuration dialog, so you can recognise them when wiring up that view's data source.

**Resulting URL example:**

```
/interfaces/{viewId}?username=john&email=john@example.com&customParam=value
```

**Reading the parameters in the target view:**

Open the Data Picker in the target view, switch to the **Search Params** tab, and pick the key you need. The placeholder resolves to the string value at runtime, for example `{{searchparams.username}}`.

---

## Forms Inside a Looping Area

When a form's input rows sit inside a looping [Area](/docs/buttons/interface-button/interface-builder/reference/layout/areas#loop), every iteration renders its own copy of each input. Two things change in the submission.

**Field names get an iteration suffix.** The first iteration keeps the original field names; every later iteration appends `_loop_N` (N starting at 1). A looped input `name` submits as `name`, `name_loop_1`, `name_loop_2`, and so on.

**The payload gains an `oc_loop` array.** One object per iteration, each grouping the looped fields' values under their base field names. The array is purely additive — the flat suffixed fields are always sent alongside it.

**JSON mode** — `oc_loop` is a genuine array inside `data`:

```json
{
  "data": {
    "name": "ads",
    "name_loop_1": "muu",
    "oc_loop": [
      { "name": "ads" },
      { "name": "muu" }
    ]
  },
  "meta": { "...": "..." }
}
```

**Multipart/Form-Data mode** — multipart is a flat transport, so the array travels as a JSON string in a `data_oc_loop` part:

```
data_name: "ads"
data_name_loop_1: "muu"
data_oc_loop: "[{\"name\":\"ads\"},{\"name\":\"muu\"}]"
```

**With `Omit oneclick metadata` on:** `oc_loop` is form data, not metadata, so it stays — in JSON mode inside the flat body, in multipart mode under the bare `oc_loop` part name.

**Rules:**

- The key is present only when the submitted form contains looped fields. Zero iterations remove the looped rows entirely — no fields, no `oc_loop` key.
- All loops in one form share the single `oc_loop` array, grouped by iteration index.
- File inputs are excluded from the array — files arrive as flat fields only.
- Disabled looped inputs contribute their default values like any other looped field.
- `oc_loop` is a reserved name. A form input the admin names `oc_loop` is overwritten by the generated array.
- A field name that merely looks looped (an input manually named `something_loop_1`) stays out of the array — only fields rendered by a loop participate.
- **Open view with form data** is unaffected: the URL carries the flat suffixed fields only, never an `oc_loop` parameter.

---

## Comparison

| Aspect | JSON | Multipart/Form-Data | Open view with form data |
|---|---|---|---|
| HTTP request | POST | POST | None |
| Content-Type | `application/json` | `multipart/form-data` | — |
| Files | Base64 inside JSON | Binary parts | Not allowed |
| File budget per submission | 10 MB | 100 MB | — |
| Submit URL | Required | Required | Ignored |
| After submission | Both options | Both options | Select view only |
| Context data (default) | `meta` object | `meta_*` parts | Not sent |
| Input values (default) | `data` object | `data_*` parts | URL search params |
| Looped fields | `data.oc_loop` array + flat suffixed fields | `data_oc_loop` JSON string + flat suffixed parts | Flat suffixed URL params only |
| With `Omit oneclick metadata` | Body is just the inner `data` object | No `meta_*` parts; `data_` prefix dropped from input parts | n/a |

---

## What the Backend Should Return

When the submission actually calls the server (JSON and Multipart/Form-Data), the response controls what the user sees next — a toast, an inline alert that replaces the form, or a navigation. See [HTTP Response Format](/docs/buttons/interface-button/interface-builder/reference/data-binding/http-response-format) for the exact shapes.
