Skip to content
Draft
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
179 changes: 179 additions & 0 deletions docs/specification/additional-fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
<!--
Copyright 2026 UCP Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# Additional Fields Extension

## Overview

The additional fields extension enables businesses to request checkout inputs
that are not covered by the core schema, and to bind those inputs to cart,
buyer, or fulfillment data via JSONPath.

**Key features**

- Declare required and optional additional fields with typed UI controls
- Map each field to cart, buyer, or fulfillment data using JSONPath
- Receive input in a consistent structure keyed by field `key`
- Surface validation via `messages[]` with JSONPath to the field value

**Dependencies**

- Checkout capability

## Discovery

Businesses advertise additional fields support in their profile:

```json
{
"ucp": {
"version": "2026-01-11",
"capabilities": {
"dev.ucp.shopping.additional_fields": [
{
"version": "2026-01-11",
"extends": "dev.ucp.shopping.checkout",
"spec": "https://ucp.dev/specification/additional-fields",
"schema": "https://ucp.dev/schemas/shopping/additional_fields.json"
}
]
}
}
}
```

## Schema

When this extension is active, checkout gains an `additional_fields` object with
field definitions and buyer-provided values.

### Additional Fields

{{ extension_schema_fields('additional_fields.json#/$defs/additional_fields', 'additional-fields') }}

### Field Definition

{{ extension_schema_fields('additional_fields.json#/$defs/additional_field', 'additional-fields') }}

### Validation Hints

{{ extension_schema_fields('additional_fields.json#/$defs/validation_hints', 'additional-fields') }}

## Input types and values

- `single_line_text` and `multi_line_text`: string values. Platforms **SHOULD**
honor `min_length`, `max_length`, and `pattern` hints when rendering input.
- `checkbox`: boolean values.
- `date`: RFC 3339 full-date string (e.g., `2026-02-14`).
- `date_time`: RFC 3339 date-time string with offset (e.g.,
`2026-02-14T15:04:05Z`).
- `default_value` must match the field's input type.

## Validation

- Businesses validate submitted values and return issues in `messages[]` using
`type: "error"` with `severity: "recoverable"` and `path` set to the field
value (e.g., `$.additional_fields.values.delivery_date`).
- No new message type is introduced; `type: "error"` + `severity` communicates
that the issue is blocking until resolved.
- Suggested error codes: `additional_field_required`, `additional_field_invalid`
(type or format mismatch), `additional_field_not_supported` (key unknown).
- The `validation_hints` object is a **hint** for platforms to improve UX;
businesses remain the source of truth and enforce rules via `messages[]`.

## Operations

- **Create/Update Checkout:** Platforms send buyer input in
`additional_fields.values`. Submitting the object replaces previously sent
values. Send an empty object to clear all additional fields.
- **Responses:** Businesses return `additional_fields.fields` to describe what
is supported, and may echo `additional_fields.values` received from the
platform.
- **Mapping:** `path` is a JSONPath from the checkout root to the data the field
influences (e.g., buyer attributes, line items, fulfillment options). It
allows platforms to render the field near its context while keeping the core
schema unchanged.

## Example

```json
{
"additional_fields": {
"fields": [
{
"key": "po_number",
"label": "PO number",
"description": "Optional purchase order for your records",
"type": "single_line_text",
"path": "$.buyer",
"validation_hints": {
"max_length": 24
}
},
{
"key": "gift_wrap",
"label": "Gift wrap",
"type": "checkbox",
"default_value": false,
"path": "$.line_items[?(@.id=='shirt')]"
},
{
"key": "delivery_date",
"label": "Delivery date",
"type": "date",
"required": true,
"path": "$.fulfillment.methods[0].groups[0]",
"validation_hints": {
"min_date": "2026-02-01"
}
},
{
"key": "gift_message",
"label": "Gift message",
"type": "multi_line_text",
"path": "$.line_items[?(@.id=='gift_box')]"
},
{
"key": "agree_to_terms",
"label": "Agree to terms",
"type": "checkbox",
"required": true,
"path": "$"
}
],
"values": {
"po_number": "PO-45822",
"gift_wrap": true,
"delivery_date": "2026-02-05",
"gift_message": "Happy birthday, Alex!",
"agree_to_terms": true
}
},
"messages": [
{
"type": "error",
"code": "additional_field_required",
"severity": "recoverable",
"path": "$.additional_fields.values.delivery_date",
"content": "Select an eligible delivery date."
}
]
}
```

Use cases include buyer birthdays, order notes, line-item personalization
(e.g., monogramming), delivery dates, gift wrap and gift messages, PO numbers,
tax IDs, and terms acceptance.
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ nav:
- EP Binding: specification/embedded-checkout.md
- AP2 Mandates Extension: specification/ap2-mandates.md
- Buyer Consent Extension: specification/buyer-consent.md
- Additional Fields Extension: specification/additional-fields.md
- Discounts Extension: specification/discount.md
- Fulfillment Extension: specification/fulfillment.md
- Cart Capability:
Expand Down
172 changes: 172 additions & 0 deletions source/schemas/shopping/additional_fields.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucp.dev/schemas/shopping/additional_fields.json",
"name": "dev.ucp.shopping.additional_fields",
"title": "Additional Fields Extension",
"description": "Extends Checkout with merchant-defined additional fields mapped to cart, buyer, or fulfillment data via JSONPath.",
"$defs": {
"input_type": {
"type": "string",
"description": "UI control type for the additional field.",
"enum": [
"single_line_text",
"multi_line_text",
"checkbox",
"date",
"date_time"
]
},
"validation_hints": {
"type": "object",
"description": "Optional validation hints for platforms. Enforcement is communicated via messages.",
"properties": {
"pattern": {
"type": "string",
"description": "Regular expression hint for text inputs."
},
"min_length": {
"type": "integer",
"minimum": 0,
"description": "Minimum characters for text inputs."
},
"max_length": {
"type": "integer",
"minimum": 1,
"description": "Maximum characters for text inputs."
},
"min_date": {
"type": "string",
"format": "date",
"description": "Earliest allowed date for date inputs (RFC 3339 full-date)."
},
"max_date": {
"type": "string",
"format": "date",
"description": "Latest allowed date for date inputs (RFC 3339 full-date)."
},
"min_date_time": {
"type": "string",
"format": "date-time",
"description": "Earliest allowed timestamp for date_time inputs (RFC 3339 date-time)."
},
"max_date_time": {
"type": "string",
"format": "date-time",
"description": "Latest allowed timestamp for date_time inputs (RFC 3339 date-time)."
}
}
},
"additional_field": {
"type": "object",
"description": "Definition for a merchant-requested input.",
"required": [
"key",
"label",
"type",
"path"
],
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]+$",
"description": "Stable identifier for the field. Used in values object keys and in messages.path (e.g., $.additional_fields.values.po_number)."
},
"label": {
"type": "string",
"description": "Buyer-facing label for the field."
},
"description": {
"type": "string",
"description": "Additional context or instructions for the buyer."
},
"type": {
"$ref": "#/$defs/input_type"
},
"required": {
"type": "boolean",
"default": false,
"description": "If true, checkout remains incomplete until a value is provided."
},
"default_value": {
"description": "Default value used when the platform does not provide buyer input.",
"oneOf": [
{
"type": "string"
},
{
"type": "boolean"
}
]
},
"path": {
"type": "string",
"description": "RFC 9535 JSONPath to the target checkout data (e.g., buyer, cart totals, fulfillment option, or specific line items)."
},
"validation_hints": {
"$ref": "#/$defs/validation_hints"
}
}
},
"values": {
"type": "object",
"description": "Buyer-provided values keyed by additional field key.",
"propertyNames": {
"pattern": "^[A-Za-z0-9_-]+$"
},
"additionalProperties": {
"oneOf": [
{
"type": "string"
},
{
"type": "boolean"
}
]
}
},
"additional_fields": {
"type": "object",
"description": "Definitions provided by the business and values supplied by the platform.",
"properties": {
"fields": {
"type": "array",
"items": {
"$ref": "#/$defs/additional_field"
},
"description": "Available fields and how they map into checkout data.",
"ucp_request": "omit"
},
"values": {
"$ref": "#/$defs/values",
"ucp_request": {
"create": "optional",
"update": "optional",
"complete": "omit"
}
}
}
},
"dev.ucp.shopping.checkout": {
"title": "Checkout with Additional Fields",
"description": "Checkout extended with merchant-defined additional fields.",
"allOf": [
{
"$ref": "checkout.json"
},
{
"type": "object",
"properties": {
"additional_fields": {
"$ref": "#/$defs/additional_fields",
"ucp_request": {
"create": "optional",
"update": "optional",
"complete": "omit"
}
}
}
}
]
}
}
}