Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions change_log.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### 1.1.3
- gravity-forms-abilities: Document repeater field creation, repeatable child-field discovery, and flattened child-array submission values.

### 1.1.2 | 2026-08-14
- gravity-forms-abilities: Add a WordPress multisite section covering per-subsite endpoints and tool allowlists, per-subsite capabilities, HTML filtering for non-super-admin users, and network-inherited license info.

Expand Down
10 changes: 9 additions & 1 deletion skills/gravity-forms-abilities/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ The agent does not need to know which mode is active — the MCP client handles
3. **Include a `notifications` object** — `forms-create` does NOT auto-create a default admin notification (unlike the GF admin UI). Without one, submissions are saved but no email is sent.
4. Call `forms-create` — returns `form_id` and `edit_url`

Never guess field types. `system-field-types` returns `supports_choices`, `has_inputs`, `default_inputs`, and support flags for each type.
Never guess field types. `system-field-types` returns `supports_choices`, `has_inputs`, `default_inputs`, `repeatable`, and support flags for each type. For `repeater`, it also reports `supports_nested_fields` and `nested_fields_property: "fields"`.

**Repeater fields.** Create repeaters with `type: "repeater"` and put child field definitions in the repeater's nested `fields` array. Child field IDs must be unique across the whole form, not just inside the repeater. If you omit child IDs, the abilities layer assigns globally unique IDs. Only use child field types where `system-field-types` reports `repeatable: true`; non-repeatable fields such as `page`, `consent`, `fileupload`, and `shipping` are rejected inside repeaters. See [references/field-config.md](references/field-config.md) §Repeater for examples.

**International phone field (Gravity Forms 3.0).** The `phone` field is still one type, but it has a `phoneFormat` setting with exactly three valid values: `"standard"` (US-masked plain string), `"international"` (unformatted plain string), or `"formatted"` (international UI, paired with a `defaultCountry` like `"us"`). The `system-field-types` phone entry reports the site's valid values as `format_options` — use those exact strings and never invent or abbreviate others; an unknown `phoneFormat` breaks form rendering. A **formatted** phone does NOT store a plain string — its value is a JSON object with the keys `country`, `national`, `formatted`, and `e164` (the `e164` value is validated against the E.164 standard). This changes how every entry-facing ability handles it:
- **Creating the field** (`forms-create` / `forms-update`): ALWAYS set `phoneFormat` explicitly — `"standard"` for a US phone, `"formatted"` (plus `defaultCountry`) for international. Do not omit it: older Gravity Forms versions do not backfill an omitted format on API-created fields, which breaks form rendering.
Expand Down Expand Up @@ -143,6 +145,8 @@ For most compound fields (name, address), use dot-notation sub-input keys: `inpu

For **multiselect fields**, pass values as an array: `"input_3": ["Red", "Blue"]`. Never use comma-separated strings — values containing commas cause data loss.

For **repeater fields**, submit values on the child input names as arrays, not on the repeater field ID. Example: if repeater `1` contains child text field `2`, submit `"input_2": ["Alice", "Bob"]` for two rows. Compound child fields use one array per sub-input, e.g. `"input_3_3": ["Alice", "Bob"]` and `"input_3_6": ["Smith", "Jones"]` for a Name child field. See [references/entry-operations.md](references/entry-operations.md) §Repeater Submission Values for nested repeater examples.

**`submissions-submit` vs `entries-create`:**
- `submissions-submit` = full pipeline (validation → entry → notifications → feeds)
- `entries-create` = raw DB insert, bypasses everything — use only for data migration
Expand Down Expand Up @@ -354,6 +358,8 @@ For CL structure details, operators, and common patterns, see [references/condit
| Missing `choices` on select/radio/checkbox | Field renders empty | Check `supports_choices` from `system-field-types` |
| Wrong input key format: `"1"` vs `"input_1"` | Silent data loss | `submissions-submit` uses `input_{id}`, `entries-create` uses `"{id}"` |
| Compound field without sub-input suffixes | Data not captured | Use `default_inputs` from `system-field-types`; for time fields send Hour/Minute/AM-PM together |
| Submitting repeater values on the repeater field ID | Rows are empty or validation fails | Submit arrays on child input names, e.g. `"input_2": ["Alice", "Bob"]`, not `"input_1"` |
| Adding a non-repeatable field inside a repeater | `forms-create` / `forms-update` rejects the form | Check `repeatable: true` from `system-field-types` before adding a child field |
| Not checking `is_valid` after submission | Miss validation failures | Always check response `is_valid` field |
| Using `@example.com` emails in submissions | Rejected as spam by GF email field | Use realistic test domains (e.g., `@testmail.dev`) |
| Passing `form_id` as top-level param on `forms-update` | Input validation error | Put form ID inside the `form` object as `id` |
Expand All @@ -379,6 +385,8 @@ For CL structure details, operators, and common patterns, see [references/condit

**Compound fields** (`has_inputs: true`): `name`, `address`, `time` — store values across sub-inputs with ID suffixes. Call `system-field-types` to see `default_inputs` for exact suffix mappings. **Name fields require `"nameFormat": "advanced"` and `"size": "large"`** — without these, sub-inputs render stacked instead of side-by-side.

**Repeater fields**: `repeater` — use nested `fields` arrays to define repeatable child fields. Child field types must have `repeatable: true` in `system-field-types`. Submit row values as arrays on child input names, not on the repeater field ID. See [references/field-config.md](references/field-config.md) §Repeater and [references/entry-operations.md](references/entry-operations.md) §Repeater Submission Values.

**File upload fields**: `fileupload` — configure `allowedExtensions` (comma-separated, no dots), `maxFileSize` (MB), `maxFiles`, `multipleFiles`. These properties are NOT returned by `system-field-types` — see [references/field-config.md](references/field-config.md) for full type-specific config reference. File upload fields can be configured via abilities, but actual file submission requires the rendered form (MCP cannot transport binary data).

**Consent fields**: `consent` — GDPR-style checkbox with `checkboxLabel` (the agreement text next to the checkbox) and `description` (longer explanatory text below). Always set `isRequired: true` for GDPR compliance. See [references/field-config.md](references/field-config.md) for examples.
Expand Down
69 changes: 69 additions & 0 deletions skills/gravity-forms-abilities/references/entry-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### For `submissions-submit` (like a browser POST)

Key format: `input_{field_id}` for simple fields, `input_{field_id}.{suffix}` for compound fields.
`submissions-validate` uses the same `input_values` format.

```json
{
Expand Down Expand Up @@ -49,6 +50,74 @@ Each checked choice is a separate input keyed by choice index (starting at 1):

Only include checked values — omit unchecked choices entirely.

### Repeater Submission Values

Repeater rows are submitted as arrays on the child input names. Do not submit structured objects on the repeater field ID.

For a repeater field `1` containing child text field `2`, two rows are submitted like this:

```json
{
"form_id": 1,
"input_values": {
"input_2": ["Alice", "Bob"]
}
}
```

Gravity Forms stores that under the repeater field internally, but the ability input remains flattened by child input name:

```json
{
"1": [
{ "2": "Alice" },
{ "2": "Bob" }
]
}
```

Compound child fields use one array per sub-input. For a Name child field `2`, submit First and Last arrays with matching indexes:

```json
{
"form_id": 1,
"input_values": {
"input_2_3": ["Alice", "Bob"],
"input_2_6": ["Smith", "Jones"]
}
}
```

This creates two repeater rows:

```json
[
{ "2.3": "Alice", "2.6": "Smith" },
{ "2.3": "Bob", "2.6": "Jones" }
]
```

Nested repeater forms still use the same principle: submit values on child input names, not as nested row objects on the repeater field ID. Use `forms-get` to confirm assigned child field IDs before building the submission payload.

Avoid these shapes:

```json
{
"input_1": [
{ "2": "Alice" },
{ "2": "Bob" }
]
}
```

```json
{
"input_1": ["Alice", "Bob"]
}
```

Use the first rejected shape only when working directly with raw stored entry data, not with `submissions-submit` or `submissions-validate`.

## Entry Search

### Filter Operators
Expand Down
66 changes: 66 additions & 0 deletions skills/gravity-forms-abilities/references/field-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Choice field types: `select`, `radio`, `checkbox`, `multiselect`, `image_choice`
| `name` | Name | No | **Yes** | prefix/first/middle/last/suffix sub-inputs |
| `address` | Address | No | **Yes** | street/city/state/zip/country sub-inputs |
| `time` | Time | No | **Yes** | hour/minute/AM-PM sub-inputs |
| `repeater` | Repeater | No | No | nested `fields` array for repeatable groups |
| `hidden` | Hidden | No | No | not visible, good for tracking data |
| `website` | Website | No | No | URL validation, conditional logic |
| `fileupload` | File Upload | No | No | `allowedExtensions`, `maxFileSize`, `maxFiles` — see Type-Specific Config |
Expand Down Expand Up @@ -269,6 +270,71 @@ Example — GDPR consent checkbox:
```
The `is` operator does not work for individual multiselect values — it matches the entire stored string.

### Repeater (`type: repeater`)

Repeater fields contain child fields in a nested `fields` array. The child field definitions use the same shape as top-level fields.

| Property | Type | Description |
|---|---|---|
| `fields` | array | Child field definitions. Required for a useful repeater. |
| `maxItems` | integer | Maximum number of rows. Omit or use `0` for no explicit limit. |
| `addButtonText` | string | Custom Add button text. |
| `removeButtonText` | string | Custom Remove button text. |
| `repeaterRowLabel` | string | Row label used in the UI, e.g. `Participant`. |
| `showRepeaterRowLabel` | boolean | Whether row labels are visibly shown. |

Rules:

- Call `system-field-types` first and only use child field types with `repeatable: true`.
- Child field IDs must be unique across the whole form, including top-level fields and all nested repeater children.
- If child IDs are omitted, the abilities layer assigns globally unique IDs.
- Nested repeaters are supported by using `type: "repeater"` inside another repeater's `fields` array.
- Layout properties and deprecated Ready Class stripping work recursively on child fields.
- Pricing child fields are scoped to their current repeater. A `quantity` or `option` child field links to a `product` child field in the same repeater scope, not an unrelated top-level product.

Simple repeater example:

```json
{
"type": "repeater",
"label": "Participants",
"repeaterRowLabel": "Participant",
"maxItems": 4,
"fields": [
{ "type": "text", "label": "Company" },
{ "type": "name", "label": "Participant Name", "isRequired": true },
{ "type": "email", "label": "Participant Email", "isRequired": true }
]
}
```

Nested repeater example:

```json
{
"type": "repeater",
"label": "Registrations",
"repeaterRowLabel": "Registration",
"fields": [
{ "type": "name", "label": "Registrant", "isRequired": true },
{
"type": "repeater",
"label": "Guests",
"repeaterRowLabel": "Guest",
"fields": [
{ "type": "text", "label": "Guest Name", "isRequired": true },
{ "type": "select", "label": "Meal", "choices": [
{ "text": "Standard", "value": "standard" },
{ "text": "Vegetarian", "value": "vegetarian" }
]}
]
}
]
}
```

Repeater submission examples are in [entry-operations.md](entry-operations.md) §Repeater Submission Values.

## Form Design Patterns

### Contact Form
Expand Down
Loading