Skip to content

Validation Rules ​

What values are valid for each field type.

How to read this doc: Rules marked Backend are enforced by Laravel FormRequest classes in upvendo-backend (cited as file:line). Rules marked UI-only are enforced only by a front-end validator in upvendo-backoffice and are not re-checked server-side. The backend is deliberately permissive: most item / category / location text fields are only required/nullable + string with no length limit, and most numbers are only numeric/integer with no min/max range unless listed below. A handful of branding and online-ordering text fields do carry a max — see "Fields that do have a max length".


Text Fields ​

Location Name ​

  • Backend: required|string — no min/max length.
  • Source: app/Http/Requests/BackOffice/Settings/Location/StoreLocationRequest.php:111 ('name' => ['required', 'string'])

Item Name ​

  • Backend: required|string — no min/max length, any characters.
  • Source: app/Http/Requests/BackOffice/Item/StoreItemRequest.php:87 ('name' => ['required', 'string'])

Category Name ​

  • Backend: required|string (nested under details).
  • Source: app/Http/Requests/BackOffice/Category/StoreCategoryRequest.php:42 ('details.name' => 'required|string')

Description (item / category / location) ​

  • Backend: nullable|string — optional, no max length.
  • Sources:
    • Item: app/Http/Requests/BackOffice/Item/StoreItemRequest.php:101 ('description' => 'nullable|string')
    • Category: app/Http/Requests/BackOffice/Category/StoreCategoryRequest.php:43 ('details.description' => 'nullable|string')
    • Location: app/Http/Requests/BackOffice/Settings/Location/StoreLocationRequest.php:120 ('description' => 'nullable|string')
  • Note: Loyalty program description is the exception — required|string (app/Http/Requests/BackOffice/Loyalty/StoreLoyaltyRequest.php:198).

Coupon / Offer Discount Code ​

  • Backend: <required-or-nullable>|string + unique-in-connection. No min length, no max length, no uppercase conversion. (nullable when the offer is saved as a draft, otherwise required.) Only present when the offer method is CODE.
  • Source: app/Http/Requests/BackOffice/Offer/StoreOfferRequest.php:55-65 ($rules['discount_code'] = [$necessity, 'string', new UniqueInConnectionWithModel(...)])
  • UI-only: back-office caps length at 10 characters. No uppercasing.
    • Source: upvendo-backoffice/src/views/marketing/offers/components/create-offers/OfferInformation.vue:50 ((v) => v?.length <= 10 || ...)

Fields that do have a max length ​

FieldRuleSource
slogannullable|string|max:100Settings/Branding/UpdateBrandingProfileRequest.php:46
short_descriptionnullable|string|max:150…/UpdateBrandingProfileRequest.php:47
wallet_program_namenullable|string|max:50…/UpdateBrandingProfileRequest.php:67
custom_domainnullable|string|max:255 (+ a closure check)StoreOnlineOrderingRequest.php:223
payment_methods.pos_cod_payment_codenullable|string|max:64StoreOnlineOrderingRequest.php:239
payment_methods.mplus_invoice_payment_codenullable|string|max:64StoreOnlineOrderingRequest.php:241
welcome_popup.messagenullable|string|max:1000StoreOnlineOrderingRequest.php:244

Advance Orders (lead time) ​

FieldRuleSource
lead_time_daysnullable|integer|min:0|max:365StoreItemRequest.php:146
available_pickup_daysnullable|arrayStoreItemRequest.php:147
available_pickup_days.*required + Rule::in(Carbon::getDays())StoreItemRequest.php:148

The same pair exists on the category request.

Device Profile — menu columns ​

FieldRuleSource
menu_item_columnsnullable|integer|in:2,3 (defaults to 2)StoreDeviceProfileRequest.php:211, default at :64

Number Fields ​

The backend does not define ranges for most numeric settings. The values below are the only numeric constraints found in FormRequest rules(). Fields from older revisions of this doc (prep time 1–180, delay range, idle-timeout default/max, delivery-radius default, tip default/array-count, loyalty points-per-currency / point-value, KDS timer ranges, volume range) have no matching backend rule and are not listed unless a real rule exists.

Order Capacity ​

All on StoreOnlineSettingsRequest:

FieldRule
order_capacity.time_slot_duration_minrequired|numeric|gt:0 (the back office restricts the choice to 5/10/15/20/30/60; a stored value outside the list, e.g. the retired 45, is still offered until changed)
order_capacity.number_of_orders_per_time_slotnullable by default, upgraded to required|numeric|gt:0 when limit_orders_per_time_slot is set (:202)
order_capacity.number_of_items_per_time_slotsame pattern, keyed on limit_items_per_time_slot
order_capacity.limit_value_per_time_slotnullable|boolean
order_capacity.max_value_per_time_slotnullable|numeric|gt:0, upgraded to required|numeric|gt:0 when limit_value_per_time_slot is set
order_capacity.value_threshold_per_time_slotnullable|numeric|gt:0
order_capacity.allow_up_tonullable|integer|gt:0
order_capacity.time_specific_rules.*.number_of_orders_per_time_slotnullable|numeric|gt:0
order_capacity.time_specific_rules.*.max_value_per_time_slotnullable|numeric|gt:0
order_capacity.slot_overridesbail|nullable|array + validateSlotOverrideShape: keys are full day names, inner keys HH:MM, each leaf {orders: int ≥ 0} (0 = slot closed for online orders)
order_capacity_splitnullable|boolean
order_capacity_by_option, .pickup, .deliverynullable|array unconditionally; while order_capacity_split is true the whole order_capacity.* rule set above is applied to each channel the client sent, keyed order_capacity_by_option.<channel>.*

The request also normalises "enabled but empty/invalid" states before validating: a flexible-limit or category-limit toggle that is on but has no usable values is silently turned off rather than rejected.

Price / Item Pricing ​

  • Backend: required|numeric + MoneyFormat rule. No min, no max, no fixed decimal count.
  • The MoneyFormat rule switches on the X-Price-Format request header (falling back to config('upvendo.pricing_format'), default 'cents'):
    • cents → value must be an integer (cents).
    • anything else (legacy) → value must be numeric (float/decimal allowed).
  • Sources:
    • app/Http/Requests/BackOffice/Item/StoreItemRequest.php:116 ('price' => ['required', 'numeric', new MoneyFormat]), :125 ('pricing.*' => ['required', 'numeric', new MoneyFormat])
    • app/Rules/Money/MoneyFormat.php:15-29
    • config/upvendo.php:6 (pricing_format, default 'cents')
  • Note: modifier prices are required|numeric only — no MoneyFormat. (app/Http/Requests/BackOffice/Modifier/StoreModifierGroupRequest.php:101)

Prep Time (item) ​

  • Backend: prep_time_seconds = nullable|numeric — no min/max, no default. (UI may send prep_time_minutes, which the request converts to seconds.)
  • Stored but not applied: item prep time is gated by the platform's item-prep-time flag (config/upvendo.php:26, default false), which is not set in production, staging or testing.
  • Source: app/Http/Requests/BackOffice/Item/StoreItemRequest.php:141 ('prep_time_seconds' => 'nullable|numeric'); conversion at :48-54.

Average Prep Time (Location) ​

  • Backend: required|integer — no min/max. Defaults to 20 if omitted (set in prepareForValidation).
  • The 20 is now one shared constant, Constants::DEFAULT_LOCATION_AVERAGE_PREP_TIME_MINUTES (app/Constants.php:419), and every path that creates a location stamps it: the back-office form, the MplusKassa import (app/Services/BackOffice/MplusKassaIntegrationService.php:5825), the ShopCaisse import (app/Services/BackOffice/ShopCaisse/ShopCaisseLocationFactory.php:53) and the Square import (app/Services/BackOffice/Square/SquareLocationSync.php:269), which previously wrote no value at all.
  • It is not a read fallback. A location document that has no average_prep_time field reads back as 0, not 20 (app/RawFactories/LocationFactory.php:32) — so a location created before this was unified quotes a 0-minute prep until someone saves a value on it.
  • Source: app/Http/Requests/BackOffice/Settings/Location/StoreLocationRequest.php:137 ('average_prep_time' => 'required|integer'); default at :56-58.

Fulfillment Delay (takeout / delivery / eat-in) ​

  • Backend: required|numeric for takeout and delivery (only when that fulfillment type is enabled); nullable|numeric for eat-in. No min/max on any of them.
  • No min means negative values are accepted, and they are meant to be: a negative delay shortens the lead time for that channel. Only the finished prep figure is clamped, to 0-1440 minutes (app/RawModels/Location.php:2015-2017, ceiling at :60).
  • Source: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:256,260 ('takeout.delay' => 'required|numeric', 'delivery.delay' => 'required|numeric') and :155 ('eat_in.delay' => 'nullable|numeric')

Idle Timeout ​

  • Backend: idle_timeout_seconds and show_warning_for_seconds = required_if:idle_timeout_type,custom|numeric|min:1. Min 1; no max, no default.
  • Source: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:202-203

Scheduled Orders — Days in Advance ​

  • Backend: required|numeric|max:365 (per fulfillment type, when enabled — takeout, delivery and eat_in). Max 365; no min.
  • Source: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:277,281,285 ('...days_in_advance' => 'required|numeric|max:365')

Scheduled Orders — Hours in Advance / Minutes Before Pickup ​

  • Backend: required|numeric|min:1 (when custom scheduled orders enabled). Min 1; no max.
  • Source: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:251-252

Delivery Areas ​

  • Backend:
    • delivery_region.zones = nullable|array|max:10 — at most 10 areas, and a withValidator check rejects a list in which every area is an exclusion ("At least one delivery zone must be a covered area.").
    • delivery_region.zones.*.id = required|string; delivery_region.zones.*.name = required|string|max:80.
    • delivery_region.zones.*.shape = required|string|in:radius,distance,postcode,street.
    • delivery_region.zones.*.exclude = nullable|boolean.
    • delivery_region.zones.*.fee = nullable|numeric|min:0, plus prohibited_if:delivery_region.zones.*.exclude,true,1 — an exclusion area may not carry a fee. Null means "inherit the default fee", which is not the same as 0.
    • Each shape's payload is both required for its own shape and prohibited_unless on the others: radius and max_distance = numeric|min:0.1; postal_codes = array|min:1 (each entry required|string); streets = array|min:1.
  • Source: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:212-230,420-440

Delivery Distance / Radius (legacy, pre-areas) ​

  • Applied only while delivery_region.zones is empty; once a location has areas the per-mode rules below stop applying.
  • Backend:
    • delivery_region.max_distance = required|numeric|min:1 (distance region type).
    • delivery_region.radius = required|numeric (other types) — no min/max.
  • Source: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:445,455,457

Delivery Fee — Custom Fees ​

  • delivery_fee.custom_fees is prohibited once a location has delivery areas — per-area prices live on delivery_region.zones[].fee instead, and a stale payload is rejected rather than silently ignored.
  • While the areas list is still empty the legacy per-region-type rules apply (postal_code / street_id keyed rows).
  • Source: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:477,483-507

Delivery Fee — Distance Pricing ​

  • Backend:
    • flat_rate_per_km = required|numeric|min:0.
    • distance_fees.*.max_km = required|numeric|gt:0; distance_fees.*.fee = required|numeric|min:0.
  • Source: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:514,516-518

Minimum Order Amount ​

  • Backend: required|numeric (when enabled). No min/max.
  • Source: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:325,329 ('minimum_order_amount.amount', 'pickup_minimum_order_amount.amount')

Tip Options (in-house dine-in) ​

  • Backend: each tip option = numeric|min:0|max:100 (when collect-tips enabled). Range 0–100; no array-count limit; no default values.
  • Source: app/Http/Requests/BackOffice/InHouseSettings/StoreInHouseSettingsRequest.php:60 ('collect_tips.options.*' => [$collectTipsNecessity, 'numeric', 'min:0', 'max:100'])

Loyalty — Points / Spend ​

  • Backend: points_earned_each_visit, points_earned_each_dollar, minimum_spend_to_earn_points, sign_up_bonus_points are all nullable|numeric (conditionally required). No min/max ranges.
  • Source: app/Http/Requests/BackOffice/Loyalty/StoreLoyaltyRequest.php:110-113,201
  • Reward discount_value (percentage) = required|numeric|gt:0|lte:100; (amount) = required|numeric|gt:0.
  • Source: app/Http/Requests/BackOffice/Loyalty/StoreLoyaltyRequest.php:179-181

KDS Timers / Preparation Time / Volume ​

  • Backend: required|integer for preparation_time_seconds, the for_here / to_go / pickup caution_time_seconds / late_time_seconds pairs, and sound.volume. No min/max ranges.
    • The timers.delivery bucket is the exception: it is sometimes|array and its caution_time_seconds / late_time_seconds are required_with:timers.delivery|integer, not required|integer.
    • preparation_time_seconds has no validation default. On KDS-profile create it is seeded from the location's average_prep_time x 60, falling back to Constants::DEFAULT_KDS_PREPARATION_TIME_SECONDS = 900 (15 min) when that is absent or zero (app/Services/KitchenDisplay/KdsProfileService.php:507-519, app/Constants.php:388). It is a seed, not a live link — later edits to the location do not move a stored profile.
  • Source: app/Http/Requests/KitchenDisplay/UpdateSettingsRequest.php:56,59-66,74-76,82

Offer Discount Value ​

  • Backend:
    • Percentage unit: required|numeric|gt:0|lte:100.
    • Amount unit: required|numeric|gt:0.
  • Source: app/Http/Requests/BackOffice/Offer/StoreOfferRequest.php:177-181

Offer Usage Limits ​

  • Backend: limit_discount_usage_amount, minimum_purchase_amount, minimum_quantity_items, benefit_quantity, max_usage_per_order are each numeric|gt:0 (when the enabling flag is set). No max.
  • Source: app/Http/Requests/BackOffice/Offer/StoreOfferRequest.php:163,172,174,185 (and :107 benefit_quantity)

Contact Fields ​

Email ​

  • Backend: Laravel email rule. nullable|email (customer) or required|email (location contact). No max length, no RFC-5322 claim.
  • Sources:
    • app/Http/Requests/BackOffice/Customer/StoreCustomerRequest.php:45 ('email' => 'nullable|email')
    • app/Http/Requests/BackOffice/Settings/Location/StoreLocationRequest.php:130 ('contact_information.email' => 'required|email')

Phone ​

  • Backend: regex against Constants::PHONE_REGEX = /^[+\d\s()-]+$/. This is a character-class check only — it allows digits, +, spaces, parentheses, and hyphens. No minimum/maximum digit count. Typically nullable|regex:... (customer) or required|regex:... (location contact).
  • Sources:
    • app/Constants.php:798 (public const PHONE_REGEX = "/^[+\d\s()-]+$/";)
    • app/Http/Requests/BackOffice/Customer/StoreCustomerRequest.php:46 ('phone' => 'nullable|regex:'.Constants::PHONE_REGEX)
    • app/Http/Requests/BackOffice/Settings/Location/StoreLocationRequest.php:131 ('contact_information.phone' => 'required|regex:'.Constants::PHONE_REGEX)

URL ​

  • No backend FormRequest rule found for a generic merchant-facing URL field. custom_domain (online ordering) is nullable|string|max:255 plus a subdomain-must-have-≥3-parts closure (not a URL/url rule).
    • Source: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:223-232

Address Fields ​

Address rules differ slightly between the back-office customer address and the location address. Both are shown.

Customer Address ​

  • country — Backend required|string.
  • address (line 1) — Backend required|string.
  • city — Backend nullable|string (optional here).
  • postal_code — Backend nullable|string (optional).
  • state, company, unit_number, first_name, last_name — nullable|string.
  • phone — nullable|regex: Constants::PHONE_REGEX.
  • Source: app/Http/Requests/BackOffice/Customer/StoreCustomerAddressRequest.php:27-37

Location Address ​

  • address.line1 — Backend required|string.
  • address.city — Backend required|string.
  • address.country — Backend required|string.
  • address.postal_code — Backend nullable|string (optional).
  • address.line2 — nullable|string.
  • Source: app/Http/Requests/BackOffice/Settings/Location/StoreLocationRequest.php:122-126

No country-specific postal-code format validation exists in the backend. Postal code is accepted as any string. The per-country format table from older revisions of this doc is removed (no rule enforces it).

Country ​

  • Backend: required|string. No in: list, no ISO-3166 enforcement at the FormRequest level for these address fields.
  • Sources: customer :27, location :116 above.

Date & Time Fields ​

Date ​

  • Backend: date_format:Y-m-d (e.g. offer dates, restricted dates, date of birth).
  • Sources:
    • app/Http/Requests/BackOffice/Offer/StoreOfferRequest.php:197,202 (active_dates.start_date / end_date)
    • app/Http/Requests/BackOffice/Settings/Location/StoreLocationRequest.php:157,162,167 (restricted dates)
    • app/Http/Requests/BackOffice/Customer/StoreCustomerRequest.php:47 (date_of_birth)

Time ​

  • Backend: date_format:H:i — 24-hour HH:MM.
  • Sources:
    • Offer: app/Http/Requests/BackOffice/Offer/StoreOfferRequest.php:198,203
    • Loyalty bonus window: app/Http/Requests/BackOffice/Loyalty/StoreLoyaltyRequest.php:210-211
    • Order cutoffs: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:158,161,164,167

Business Hours Time Range ​

  • Backend: each entry has from and to, both date_format:H:i (required when the day is_available, otherwise nullable).
  • Sources:
    • Location: app/Http/Requests/BackOffice/Settings/Location/StoreLocationRequest.php:185-186 (business_hours.{day}.times.*.from / .to)
    • Online ordering custom schedule: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:271-272
  • Note: no backend rule enforces "end must be after start" for business-hours entries.

Restricted Date ​

  • Backend: date_format:Y-m-d. Date-range entries require date_until after_or_equal:date_from. temporarily_closed_until (online ordering) is date_format:Y-m-d|after_or_equal:today.
  • Sources: app/Http/Requests/BackOffice/Settings/Location/StoreLocationRequest.php:157-172; app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:234

Select / Enum Fields ​

Several fields are constrained to enum values via Rule::enum(...) rather than the literal lists in older revisions of this doc. The doc's standalone Country Codes / Language Codes / Currency Codes / Timezone lists are not enforced by these FormRequest rules and are omitted (no in: rule found for them in the address/contact requests).

Days of Week ​

  • Backend: Rule::in(Carbon::getDays()) (full day names: Sunday–Saturday).
  • Sources: offer active_days (StoreOfferRequest.php:196); online-ordering available days (StoreOnlineOrderingRequest.php:213,215); loyalty bonus days (StoreLoyaltyRequest.php:208).

Enum-Constrained Fields (examples) ​

  • Item status — Rule::enum(ItemStatuses::class) (StoreItemRequest.php:115).
  • Item / modifier platforms / visibility — Rule::enum(ChannelOptions::class) (StoreItemRequest.php:127, StoreModifierGroupRequest.php:108).
  • Modifier pricing — Rule::enum(PricingOptions::class) (StoreModifierGroupRequest.php:87).
  • Offer type / method / discount unit — Rule::enum(OfferTypes/OfferMethods/DiscountUnits::class) (StoreOfferRequest.php:36-37,69).
  • Image source — Rule::enum(ImageSources::class) (e.g. StoreItemRequest.php:102).

Array Fields ​

Business Hours ​

  • Backend: business_hours = required|array. Each day: is_available required|boolean, times array, each time { from, to } = date_format:H:i.
  • No "max 3 times per day" limit in the backend.
  • Source: app/Http/Requests/BackOffice/Settings/Location/StoreLocationRequest.php:128,175-186

Delivery Area Postcodes ​

  • Backend: delivery_region.zones.*.postal_codes = array|min:1 (required when that area's shape is postcode, prohibited otherwise); each entry required|string.
  • No max-count limit on postcodes within an area (no "max 500"). The cap is on the number of areas — 10.
  • Legacy fallback, while the areas list is empty: delivery_region.postal_codes = required_without:delivery_region.streets|array; each entry required|string. A street-only configuration is valid.
  • Source: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:437-438,449-450

Tip Options ​

  • Backend (in-house dine-in): collect_tips.options = array; each value numeric|min:0|max:100. No min/max item count.
  • Source: app/Http/Requests/BackOffice/InHouseSettings/StoreInHouseSettingsRequest.php:59-60

Allergens / Dietary Preferences (item) ​

  • Backend: each is nullable|array; entries required|string. No "must be from a predefined list" rule server-side (free-text strings).
  • Source: app/Http/Requests/BackOffice/Item/StoreItemRequest.php:133-136

Ingredients / Dietary Supplements (item) ​

  • Backend: nullable|array; entries required|string.
  • Source: app/Http/Requests/BackOffice/Item/StoreItemRequest.php:131-132,137-138

Image Fields ​

Item / Category / Modifier / Online-Ordering Landing Image ​

  • Backend: image.file = nullable|max:10240 (≈ 10 MB; max on an uploaded file is in kilobytes). image.source = nullable enum (ImageSources).
  • No mime/format restriction on these image.file fields (no mimes: rule).
  • Sources:
    • Item: app/Http/Requests/BackOffice/Item/StoreItemRequest.php:102-103 ('image.file' => 'nullable|max:10240')
    • Category: app/Http/Requests/BackOffice/Category/StoreCategoryRequest.php:45
    • Modifier: app/Http/Requests/BackOffice/Modifier/StoreModifierGroupRequest.php:103 ('modifiers.*.image.file' => 'nullable|max:10240')
    • Online ordering landing page: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:211 ('landing_page_image.file', not image.file)

Recommended dimensions / min-dimension / per-image-type size differences (logo 2 MB, banner 10 MB, etc.) from older revisions are not enforced by any FormRequest and are removed. Where mime restrictions do exist they are on other uploads (e.g. guided-setup thumbnail mimes:jpg,jpeg,png,webp|max:5120, guided-setup video mimes:mp4,...|max:102400, app bundle mimes:zip).


Color Fields ​

  • All eight branding colour fields — primary_color, secondary_color, primary_text_color, secondary_text_color, primary_background_color, secondary_background_color, promo_code_font_color, promo_code_background_color — are required|string on update (Settings/Branding/UpdateBrandingProfileRequest.php:56-64). Required, but with no hex or format check.
  • The create request validates only name (Settings/Branding/StoreBrandingProfileRequest.php).
  • Any hex-format validation is therefore UI-only.

Special Validations ​

Modifier Group — Max Selected ​

  • Backend: settings.max_selected = required only (present when "allow select more than one" is enabled). No max:99 or numeric range. ("Allow select more than one", "is mandatory", and "allow same modifier more than once" are each required|boolean.)
  • Source: app/Http/Requests/BackOffice/Modifier/StoreModifierGroupRequest.php:104-106,119-121

Offer Discount Value Cap ​

  • Backend: percentage discounts are gt:0|lte:100; amount discounts gt:0.
  • Source: app/Http/Requests/BackOffice/Offer/StoreOfferRequest.php:177-181

Delivery Fee — Free Over Amount ​

  • Backend: when fee type is "free over certain amount", minimum_order_amount_for_free_delivery = required|numeric (no gt:0).
  • Source: app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php:303

Offer Usage Limits ​

  • Backend: limit_discount_usage_amount = numeric|gt:0 (when limit_discount_usage is on). limit_one_usage_per_customer is a boolean flag (no per-customer numeric cap field).
  • Source: app/Http/Requests/BackOffice/Offer/StoreOfferRequest.php:183-188

Error Messages ​

Backend validation messages are Laravel defaults unless a request defines a messages() method (e.g. StoreLoyaltyRequest::messages(), app/Http/Requests/BackOffice/Loyalty/StoreLoyaltyRequest.php:259-294). Exact user-facing copy comes from the back-office i18n locale files, not from a fixed table — the strings below are illustrative, not authoritative.

ValidationTypical message
Required field empty"This field is required"
Invalid email"Please enter a valid email address"
Invalid phone"Please enter a valid phone number"
File too large"File size must be less than {max}"
Invalid time range"End time must be after start time"