> For the complete documentation index, see [llms.txt](https://docs.datajet-app.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.datajet-app.com/functions/functions/cart-and-checkout-validation.md).

# Cart and Checkout Validation

A validation function decides whether the buyer may continue through checkout. Your code inspects the cart and returns a list of errors: an empty list allows checkout, any error blocks it and is shown to the buyer.

```javascript
var errors = [];

if (parseFloat(input.cart.cost.totalAmount.amount) > 5000) {
  errors.push({ message: 'Orders above 5000 are not allowed', target: '$.cart' });
}

return errors;
```

Validations run on Shopify's servers and are enforced throughout checkout, so they can't be bypassed by the client.

### Return value

Return an **array of errors**. Each error is either:

* a string — shown at the top of checkout, or
* an object `{ message, target }` — shown at a specific checkout field.

Returning an empty array allows the checkout to proceed.

Instead of the errors array you can also return the full Shopify operations shape:

```javascript
return { operations: [{ validationAdd: { errors: errors } }] };
```

### Error targets

The `target` controls where the message appears at checkout:

| Target                                              | Where it's shown          |
| --------------------------------------------------- | ------------------------- |
| `$.cart`                                            | Top of checkout (default) |
| `$.cart.buyerIdentity.email`                        | Email field               |
| `$.cart.buyerIdentity.phone`                        | Phone field               |
| `$.cart.deliveryGroups[0].deliveryAddress.address1` | Shipping address field    |
| `$.cart.billingAddress.address1`                    | Billing address field     |
| `$.cart.poNumber`                                   | PO number field           |

For the address targets, the same set of fields is available on both shipping and billing: `address1`, `address2`, `city`, `zip`, `provinceCode`, `countryCode`, `firstName`, `lastName`, `company`, `phone`.

{% hint style="warning" %}
Address and billing fields are only present in the `input` if the function's family selects them — for example the *Address*, *Billing address* or *Billing + Shipping address* families. Pick the family to match the data your rule checks. See [Families and Variables](/functions/functions/families-and-variables.md).
{% endhint %}

### Run on — checkout steps

Validations can run at up to three points of the buyer journey, chosen with the **Run on** setting:

| Step                 | When your code runs                      |
| -------------------- | ---------------------------------------- |
| Cart                 | On the cart page, before checkout starts |
| Checkout interaction | While the buyer fills in checkout        |
| Checkout completion  | At the final submit, when the buyer pays |

The default is *Checkout interaction* + *Checkout completion*. Outside the selected steps the function returns no errors and the buyer is not blocked. At least one step must be selected.

### Examples

**Order value limit:**

```javascript
var errors = [];

if (parseFloat(input.cart.cost.totalAmount.amount) > 5000) {
  errors.push({ message: 'Orders above 5000 are not allowed', target: '$.cart' });
}

return errors;
```

**Require a phone number:**

```javascript
var errors = [];

if (!input.cart.buyerIdentity || !input.cart.buyerIdentity.phone) {
  errors.push({ message: 'Please provide a phone number', target: '$.cart.buyerIdentity.phone' });
}

return errors;
```

**Block PO boxes in the shipping address** (use an address family):

```javascript
var errors = [];

(input.cart.deliveryGroups || []).forEach(function (group) {
  var address = group.deliveryAddress;
  if (address && /p\.?\s*o\.?\s*box/i.test(address.address1 || '')) {
    errors.push({
      message: 'We cannot ship to PO boxes',
      target: '$.cart.deliveryGroups[0].deliveryAddress.address1'
    });
  }
});

return errors;
```

**Limit quantity for tagged products** (use the *Product* family with the `hasProductTags` variable set to `["limited"]`):

```javascript
var errors = [];

(input.cart.lines || []).forEach(function (line) {
  var product = line.merchandise && line.merchandise.product;
  var isLimited = product && product.hasTags && product.hasTags.some(function (t) { return t.hasTag; });
  if (isLimited && line.quantity > 2) {
    errors.push({ message: 'Maximum 2 units of limited products per order', target: '$.cart' });
  }
});

return errors;
```

### Notes

* An error message blocks checkout entirely — the buyer cannot proceed until the condition is resolved.
* If your code throws, the validation is skipped and checkout continues (fail open).
* Shopify allows at most 25 active validations per store; your plan may allow fewer — see [Functions](/functions/functions.md).
* See also: [Delivery Customization](/functions/functions/delivery-customization.md), [Payment Customization](/functions/functions/payment-customization.md).
