> ## Documentation Index
> Fetch the complete documentation index at: https://learn.nexudus.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Validation rules

> Write rules that stop customers, bookings, contracts, sales and other records being saved unless they meet your conditions — for example, requiring a VAT number or limiting bookings to the half hour.

## Overview

A **validation rule** checks a record every time it's created or changed — in the dashboard, the members portal or the apps — and blocks the save with your own error message if it doesn't meet your conditions. Use rules for checks Nexudus doesn't offer as a setting, such as making a field required, checking a format, or allowing a resource to be booked only with a certain product.

Each rule is a **formula** that returns *true* or *false*. **True** lets the record be saved. **False** blocks it and shows your error message.

## How to access

Go to **Settings → Data management → Validation rules**.

<img src="https://mintcdn.com/nexudus/DRnFFtGEY-LOq5pN/images/platform/settings/settings-validation-rules.png?fit=max&auto=format&n=DRnFFtGEY-LOq5pN&q=85&s=f2869128235a2ed87a3c2cd36808be14" alt="Validation rules list" width="1440" height="900" data-path="images/platform/settings/settings-validation-rules.png" />

## Adding a rule

Select **Add rule**, then either pick one of the ready-made rules or choose to enter your own.

| Ready-made rule | Record type | What it does |
| - | - | - |
| **Make VAT number required** | Customers | New customers must have a VAT number. |
| **Validate VAT number format** | Customers | VAT numbers must match the UK format. |
| **Prevent last minute purchases** | Bookings | Customers can't add a chosen product to a booking less than 24 hours before it starts. |
| **Fixed booking times** | Bookings | Bookings must start and end on the hour or half hour. |
| **Make customer emails unique** | Customers | New customers can't use an email address that another customer already has. |
| **Require first and last name** | Customers | Customers' full names must have at least two words. |
| **Prevent multiple contracts** | Contracts | Customers can have one active contract at most. |
| **Check for full admin role** | Contracts | Only users with a full admin role can create or change contracts. |

For your own rule, fill in:

| Field | What to enter |
| - | - |
| **Name** | A name for your team. |
| **Location** | The location the rule belongs to. Rules at a network location also apply to its other locations. |
| **Description** | Optional notes on what the rule is for. |
| **This rule is active** | Turn off to pause the rule without deleting it. |
| **Record type** | What the rule checks: **Customers**, **Bookings**, **Contracts**, **Visitors**, **Product sale**, **Proposal contract**, **Invoice**, **Teams**, **Event attendee** or **Commands**. |
| **Error message to display if the validation rule is not met** | What people see when the rule blocks a save. Say what to fix. |
| **Formula** | The condition the record must meet. |

<Warning>
  A formula with an error blocks every save of that record type, with a message that the rule failed. Test each new rule straight away by doing what it should allow and what it should block — and turn it off if something goes wrong.
</Warning>

## Writing formulas

The record being saved is called `record`. You reach its fields with a dot, and follow links to other records the same way — `record.Resource.ResourceType.Id` is the ID of the booked resource's type. The fields available are the record's fields in the [Nexudus API](/rest-api/overview).

| You can use | Example |
| - | - |
| Comparisons: `=`, `<>`, `<`, `>`, `<=`, `>=` | `record.Resource.Id <> 123` |
| `AND`, `OR`, `NOT` | `record.Coworker = null OR record.Notes <> null` |
| `null` for an empty field, and `""` for empty text | `record.TaxIDNumber <> null AND record.TaxIDNumber <> ""` |
| `if(condition, value if true, value if false)` | `if(record.Coworker <> null AND record.Notes = null, false, true)` |
| `IsMatch(text, pattern)` to check a format | `IsMatch(record.MobilePhone, "\\+[1-9]\\d{1,14}")` |
| Arithmetic and dates, such as `record.FromTime.Minute` | `record.FromTime.Minute = 0` |

`OR` stops at the first true condition, so put the cheapest or broadest checks first. To find a pass, product, resource or plan ID, open it in the dashboard — the ID is in the address bar.

### Variables

| Variable | What it holds |
| - | - |
| `record` | The record being saved. |
| `isCreate` / `isUpdate` | Whether the record is new or being changed. |
| `user` | The signed-in user, such as `user.Email` or `user.IsAdmin`. |
| `isLoggedIn` | Whether someone is signed in. |
| `userIsAdmin` / `userHasAnyRoles` | Whether the signed-in user is an admin, or has any admin role — useful to let your team bypass a rule. |

### Functions

**Customers**

| Function | Returns |
| - | - |
| `EmailExists(customer)` / `EmailExistsSameType(customer)` | Whether another customer — or another customer of the same type — already uses this email address. |
| `AccessCardExists(customer)` / `AccessPincodeExists(customer)` | Whether another customer already uses this access card or PIN. |
| `MobilePhoneExists(customer)` / `CompanyNameExists(customer)` / `BillingNameExists(customer)` | Whether another customer already uses this mobile number, company name or billing name. |
| `IsInPlan(customer, planId or "plan name")` | Whether the customer has an active contract for the plan. |
| `WasInPlan(customer, planId)` / `WasIsInPlan(customer, planId)` | Whether the customer had — or has or had — a contract for the plan. |
| `IsInTeam(customer, "team name")` | Whether the customer is in the team. |
| `HasPass(customer, passId)` | Whether the customer has the pass. |
| `HasProduct(customer, productId)` / `ProductQuantity(customer, productId)` | Whether — and how many of — the product the customer has bought or has in their basket. |
| `HasPaidProduct(customer, productId)` / `PaidProductQuantity(customer, productId)` | The same, for products they've paid for. |
| `UnpaidInvoices(customer)` / `UnpaidInvoicesAmount(customer)` | How many unpaid invoices the customer has, and their total. |
| `HasBookingCredit(customer)` / `BookingCreditAmount(customer)` | Whether the customer has booking credit, and how much. |
| `HasTimeCredit(customer)` | Whether the customer has time credit. |
| `HasCheckedInAtEvent(customer, eventId)` | Whether the customer checked in at the event. |

**Bookings**

| Function | Returns |
| - | - |
| `HoursToStart(booking)` / `MinutesToBooking(booking)` / `HoursToEnd(booking)` | Time from now until the booking starts or ends. |
| `MinutesFromMidnightFromTime(booking)` / `MinutesFromMidnightToTime(booking)` | The booking's start or end time, as minutes after midnight. |
| `WeekDayName(booking)` | The day of the week the booking starts. |
| `HasOverlappingBooking(booking)` | Whether the customer already has a booking at the same time. |
| `HasOveralappingBookingByResourceId(booking, resourceId)` / `HasOveralappingBookingByType(booking, resourceTypeId)` | Whether there's an overlapping booking for that resource, or for a resource of that type. |
| `BookingsCountThisDay(booking)` | How many bookings the customer has that day. Also `…ByResource` and `…ByType`, and `BookingsCountThisWeek…` and `BookingsCountThisMonth…` versions. |
| `BookedMinutesThisDay(booking)` | How many minutes the customer has booked that day. Also `…ByResource`, `…ByType`, `…ThisWeek…` and `…ThisMonth…` versions. |
| `HasProduct(booking, productId)` / `ProductQuantity(booking, productId)` / `HasTaggedProduct(booking, "tag")` | Whether — and how many of — a product is added to the booking. |
| `HasEnoughCredit(booking)` / `HasEnoughBookingCredit(booking)` / `HasEnoughTimeCredit(booking)` | Whether the customer has enough credit to pay for the booking. |

**Other**

| Function | Returns |
| - | - |
| `IsInRole(user, "role name")` | Whether the signed-in user has the admin role. |
| `IsDirtyProperty(record, "Field1,Field2")` | Whether any of the listed fields are being changed. |
| `GetDateTimeUtcNow()` / `GetDateTimeLocalNow(record.Business)` | The current date and time. |
| `ToLocalTime(time, record)` | Converts a time to the location's local time. |
| `IsBlank(text)` | Whether the text is empty. |

Spelling matters, including in `HasOveralappingBooking…`.

## Examples

Replace the IDs with your own.

| To… | Record type | Formula |
| - | - | - |
| Require notes on bookings for a customer | Bookings | `record.Coworker = null OR record.Notes <> null` |
| Stop admins making bookings without a customer | Bookings | `record.Coworker <> null` |
| Only let customers with a pass book a resource | Bookings | `record.Resource.Id <> 123 OR record.Coworker = null OR HasPass(record.Coworker, 456)` |
| Require a product when booking a resource | Bookings | `record.Resource.Id <> 123 OR HasProduct(record, 456)` |
| Let members book a resource, but make others buy a product | Bookings | `record.Resource.Id <> 123 OR record.Coworker.IsMember() OR HasProduct(record, 456)` |
| Only add a product to bookings of at least 60 minutes | Bookings | `record.GetLengthInMinutes() >= 60 OR HasProduct(record, 456) = false` |
| Stop a resource being booked before a date | Bookings | `record.Resource.Id <> 123 OR record.FromTime > ToDateTime("2026-12-01")` |
| Block one resource while another is booked (one way only) | Bookings | `HasOveralappingBookingByResourceId(record, 123) = false OR record.Resource.Id <> 456` |
| Stop customers without booking credit booking, except contacts and your team | Bookings | `isLoggedIn = false OR record.Coworker.IsContact() OR BookingCreditAmount(record.Coworker) > 0` |
| Stop customers signing up for the same plan twice | Contracts | `record.Tariff.Id <> 123 OR WasInPlan(record.Coworker, 123) = false OR userHasAnyRoles` |
| Check mobile numbers are in international format | Customers | `IsMatch(record.MobilePhone, "\\+[1-9]\\d{1,14}")` |
| Make access card numbers 10 characters | Customers | `record.AccessCardId = null OR record.AccessCardId.Length = 10` |
| Stop customers using a network location as their home location | Customers | `record.InvoicingBusiness.RootLocation <> null` |
| Only let a team's paying member or administrator buy a product | Product sale | `record.Product.Id <> 123 OR record.Coworker.IsPayingMember() OR record.Coworker.IsTeamAdministrator()` |
| Only allow visitors hosted by members or contacts | Visitors | `record.Coworker.IsMember() OR record.Coworker.IsContact()` |
| Stop draft invoices being created | Invoice | `not(record.Draft)` |

## Related

* [Resource rules](/platform/billing/resource-rules) — booking rules you set up without formulas
* [Custom fields](/platform/crm/custom-fields) — extra fields you can check in rules


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.