> For the complete documentation index, see [llms.txt](https://docs.partssource.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.partssource.com/api/domain-concepts/required-fields.md).

# Required Fields

Handling vendor-required custom fields when placing orders

Some pricing options require additional information from the buyer before an order can be placed. These vendor-defined custom fields capture data like serial numbers, equipment models, or service details that the vendor needs to fulfill the order.

***

## Custom Field Properties

Each custom field in the `/catalog/detail` response has these properties:

| Property       | Type    | Description                                     |
| -------------- | ------- | ----------------------------------------------- |
| `fieldId`      | string  | Unique identifier—use this in the order request |
| `prompt`       | string  | User-facing label for the field                 |
| `description`  | string  | Additional context or instructions              |
| `isRequired`   | boolean | Whether the field must be provided              |
| `formatRegex`  | string  | Validation pattern (if any)                     |
| `errorMessage` | string  | Message to show if validation fails             |
| `placeholder`  | string  | Example value for the input field               |

### Example Response

```json
{
  "options": [
    {
      "priceOptionId": "OPT-ABC123",
      "price": 850.00,
      "condition": "Aftermarket",
      "customFields": [
        {
          "fieldId": "serial_number",
          "prompt": "Equipment Serial Number",
          "description": "The serial number of the equipment this part will be installed on",
          "isRequired": true,
          "formatRegex": "^[A-Z0-9]{8,12}$",
          "errorMessage": "Must be 8-12 alphanumeric characters",
          "placeholder": "e.g., ABC12345XYZ"
        },
        {
          "fieldId": "purchase_order",
          "prompt": "Internal PO Reference",
          "description": "Your internal purchase order number for tracking",
          "isRequired": false,
          "formatRegex": null,
          "errorMessage": null,
          "placeholder": null
        }
      ]
    }
  ]
}
```

***

## Submitting Required Fields

When creating an order, include the field values in `requiredFields` for each quote item:

```json
{
  "quoteItems": [
    {
      "priceOptionId": "OPT-ABC123",
      "productId": "CAT-123",
      "quantity": 1,
      "price": 850.00,
      "requiredFields": [
        {
          "fieldId": "serial_number",
          "value": "ABC12345XYZ"
        },
        {
          "fieldId": "purchase_order",
          "value": "PO-2025-0042"
        }
      ]
    }
  ]
}
```

| Property  | Type   | Description                                  |
| --------- | ------ | -------------------------------------------- |
| `fieldId` | string | Must match the `fieldId` from catalog detail |
| `value`   | string | The user-provided value                      |

***

## Validation

### Client-Side Validation

Always validate user input before submitting:

```typescript
interface CustomField {
  fieldId: string;
  prompt: string;
  isRequired: boolean;
  formatRegex: string | null;
  errorMessage: string | null;
}

interface ValidationResult {
  isValid: boolean;
  errors: Map<string, string>;
}

function validateRequiredFields(
  fields: CustomField[],
  values: Map<string, string>
): ValidationResult {
  const errors = new Map<string, string>();

  for (const field of fields) {
    const value = values.get(field.fieldId) || '';

    // Check required fields
    if (field.isRequired && !value.trim()) {
      errors.set(field.fieldId, `${field.prompt} is required`);
      continue;
    }

    // Check format if value provided and regex exists
    if (value && field.formatRegex) {
      const regex = new RegExp(field.formatRegex);
      if (!regex.test(value)) {
        errors.set(
          field.fieldId,
          field.errorMessage || `Invalid format for ${field.prompt}`
        );
      }
    }
  }

  return {
    isValid: errors.size === 0,
    errors
  };
}
```

### Server-Side Validation

The API validates required fields on order submission. Missing or invalid fields return `422 Unprocessable Entity`:

```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Required field validation failed",
    "details": [
      {
        "field": "quoteItems[0].requiredFields.serial_number",
        "message": "Must be 8-12 alphanumeric characters"
      }
    ]
  }
}
```

***

## Common Field Types

While vendors can define any custom fields, these are commonly seen:

| Field Type    | Typical fieldId                     | Purpose                                    |
| ------------- | ----------------------------------- | ------------------------------------------ |
| Serial Number | `serial_number`, `equipment_serial` | Equipment identification for compatibility |
| Model Number  | `model_number`, `equipment_model`   | Equipment model for part matching          |
| Asset Tag     | `asset_tag`, `asset_id`             | Internal asset tracking                    |
| PO Reference  | `po_number`, `purchase_order`       | Customer purchase order tracking           |
| Cost Center   | `cost_center`, `department`         | Internal accounting allocation             |
| Technician    | `technician_name`, `service_tech`   | Who will install the part                  |

***

## Handling Optional Fields

Not all custom fields are required. Check `isRequired` before enforcing validation:

```csharp
public class RequiredFieldCollector
{
    public List<RequiredField> CollectFields(
        List<PricingCustomField> customFields,
        Dictionary<string, string> userInput)
    {
        var result = new List<RequiredField>();

        foreach (var field in customFields)
        {
            var value = userInput.GetValueOrDefault(field.FieldId, "");

            // Only include if required OR if user provided a value
            if (field.IsRequired || !string.IsNullOrEmpty(value))
            {
                result.Add(new RequiredField
                {
                    FieldId = field.FieldId,
                    Value = value
                });
            }
        }

        return result;
    }
}
```

***

## UI Considerations

### Displaying Custom Fields

```typescript
function renderCustomFieldInput(field: CustomField): JSX.Element {
  return (
    <div className="form-group">
      <label htmlFor={field.fieldId}>
        {field.prompt}
        {field.isRequired && <span className="required">*</span>}
      </label>

      {field.description && (
        <p className="help-text">{field.description}</p>
      )}

      <input
        id={field.fieldId}
        name={field.fieldId}
        placeholder={field.placeholder || ''}
        required={field.isRequired}
        pattern={field.formatRegex || undefined}
      />
    </div>
  );
}
```

### Empty customFields Array

Many pricing options have no custom fields. Always check the array before rendering:

```typescript
const hasCustomFields = option.customFields && option.customFields.length > 0;

if (hasCustomFields) {
  // Render custom field form
} else {
  // Proceed directly to order confirmation
}
```

***

## Error Handling

| Error                      | Cause                  | Solution                                         |
| -------------------------- | ---------------------- | ------------------------------------------------ |
| `422 Unprocessable Entity` | Missing required field | Check `isRequired` fields have values            |
| `422 Unprocessable Entity` | Invalid format         | Validate against `formatRegex` before submitting |
| `400 Bad Request`          | Unknown fieldId        | Use exact `fieldId` from catalog detail          |
| `400 Bad Request`          | Wrong priceOptionId    | Ensure fields match the selected option          |

### Retry After Validation Error

```csharp
public async Task<OrderResponse> CreateOrderWithValidation(OrderRequest order)
{
    try
    {
        return await CreateOrder(order);
    }
    catch (ApiException ex) when (ex.StatusCode == 422)
    {
        var validationError = ParseValidationError(ex.Content);

        // Log specific field errors for user correction
        foreach (var detail in validationError.Details)
        {
            _logger.LogWarning(
                "Field validation failed: {Field} - {Message}",
                detail.Field,
                detail.Message);
        }

        throw new UserInputRequiredException(validationError.Details);
    }
}
```

***

## Best Practices

1. **Fetch fresh catalog detail** before displaying custom fields—they can change
2. **Validate client-side first** to provide immediate feedback
3. **Preserve user input** if server validation fails so users don't re-enter everything
4. **Display field descriptions** to help users understand what's needed
5. **Use placeholder text** as examples when available
6. **Handle empty arrays** gracefully—not all options have custom fields
7. **Match fieldId exactly** including case sensitivity

***

## Related Concepts

* [Price Option ID](/api/domain-concepts/price-option-id.md) - Custom fields are tied to specific pricing options
* [Product Condition](/api/domain-concepts/product-condition.md) - Certain conditions may require additional fields
* [Search to Order Cookbook](/api/cookbooks/search-to-order.md) - Complete workflow with custom fields example


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.partssource.com/api/domain-concepts/required-fields.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
