Who is this guide for?
This guide is intended for developers building external checkout experiences. If you are using the standard Checkout Widget without a custom checkout flow, this validation is performed automatically by Checkout and no additional implementation is required.
External implementations may restore an order after refresh or when the customer returns later. Without validation, they could continue to payment even though required information is still missing. The Ventrata.validate() API function solves that problem.
Recommended Approach
Ventrata provides the Ventrata.validate() API for validating whether an order is ready to proceed to payment.
For most external implementations, this is the recommended approach. It reproduces Checkout's validation behaviour without requiring you to maintain your own validation logic.
π TIP
If you cannot use Ventrata.validate(), see External Implementation: Validate an Order Before Payment (via pseudocode) for a reference implementation of Checkout's validation logic.
Using Ventrata.validate()
Ventrata.validate() provides the same order-completion validation used by Checkout for external implementations. Call it before allowing the customer to continue to payment.
The Checkout script adds the Ventrata.validate() function to the existing window.Ventrata object.
await window.Ventrata.validate(input);
π TIP
The function is asynchronous. Always use await or .then().
Use this API for:
A fully custom checkout experience
A custom cart using Ventrata orders
A hybrid checkout using both Checkout and custom pages
A flow that restores saved orders before payment
π NOTE
A website using the Ventrata Checkout flow does not need to call the Ventrata.validate() API on every Checkout page β it already runs its own series of checks.
Recommended flow
Validate the order immediately before payment, and again whenever the order has been updated.
Create or update an order.
Save the order ID.
Before payment, call
Ventrata.validate().Check
result.valid:if
true- continue to the next implemented stepif
false- read the items inresult.issues
π NOTE
Also run validation after restoring an order, as the order may not be complete.
Ask the customer to complete the missing information.
Update the order.
Call
Ventrata.validate()again.
When to Use the API
The Ventrata.validate() function operates in two modes:
Full Order Mode
Full Order Mode
Ventrata.validate({ order });
Use when the full order object is loaded on the page.
Ventrata.ordercan benull. This happens before an order exists or after it was cleared. The result will not be valid and will say that the order is missing.
Name | Required | Description |
| Yes | the full Ventrata order object to check |
|
| π NOTE Do not pass |
π EXAMPLE
π EXAMPLE
const result = await Ventrata.validate({
order: Ventrata.order,
});
Order ID Mode
Order ID Mode
Ventrata.validate({ orderId, apiKey, env });
Use when the page only saved the order ID.
The API loads the latest order from Ventrata and then checks it.
The name is orderId with a lowercase d. Some older Checkout configuration examples use orderID, but that is not the name used by this API.
Name | Required | Description |
| Yes | ID of the order to load or check |
| Yes | API key used to load the order |
|
| π NOTE The API key must be able to read the order in the selected environment. A wrong key or wrong environment returns the normal load failure result. |
| No |
|
|
| π NOTE For production, always pass |
|
| π NOTE If |
π EXAMPLE
π EXAMPLE
const result = await Ventrata.validate({
orderId: "019f6d01-c1da-71fb-9d00-2398de9f2ca1",
apiKey: "YOUR_API_KEY",
env: "live",
});π NOTE
Do not provide both a full order and orderId in the same call. If a real value is present, the full order mode is used. The order ID is ignored.
JavaScript objects sometimes contain order: undefined. This is treated as if order was not present. A valid orderId can still be used in that case.
What the API Checks
Ventrata.validate() validates the latest saved order. It checks for missing information, including:
required availability for package includes
required visible questions for a booking
required visible questions for a unit
required visible questions for a package booking
required visible questions for a package unit
required contact fields for units and package units
required pickup and dropoff points when the saved availability says they are required
required visible questions for the whole order
required contact fields for the whole order
For required questions, these values count as missing:
null;undefined;an empty string;
a string that only contains spaces.
Hidden Questions
A hidden dependent question does not block the order. It is only checked when its rules make it visible.
Contact Fields
For contact fields, empty text and empty lists count as missing.
Packages
An optional package include does not need an availability. A required include does.
Cancelled top-level bookings are ignored.
Multi-Date Packages
A multi-date package is one top-level booking with one or more package bookings inside it. The package bookings represent the included products.
A multi-date package is one top-level booking with one or more package bookings inside it. The package bookings represent the included products.
Example shape:
order
βββ top-level booking
βββ package booking A
βββ package booking B
The API can find a required package include that still has no saved date or time. It returns package-availability for that case.
This matters because a multi-date order can already exist while one of its required includes still has no selected date or time.
A saved order may already contain a pickup or dropoff point before the customer manually changes or confirms it.
π TIP
If your implementation supports multi-date packages, see How to Create Multi-Date Packages.
Multibooking
Multibooking means one order can contain several top-level bookings.
Multibooking means one order can contain several top-level bookings.
Example shape:
order
βββ booking A
β βββ optional package bookings
βββ booking B
βββ optional package bookings
The API checks every active top-level booking. One result can contain issues for more than one booking.
Each booking issue contains the parent bookingUuid. This lets the external implementation identify which booking needs attention.
The API does not create child orders for multibooking.
π TIP
If your implementation supports multibooking, see How to Use the Multibooking Feature.
Contact Requirements and Quotes
The API trusts the required contact fields saved on the order and its units. It does not load the supplier's Checkout configuration to invent extra rules.
The API trusts the required contact fields saved on the order and its units. It does not load the supplier's Checkout configuration to invent extra rules.
For example, quotesAllowed means that saving a quote is available. It does not mean that email must be required for every normal order.
βΌοΈ IMPORTANT
Ventrata.validate() checks saved data, not the page the customer is currently viewing. The returned step indicates the type of missing information, not the last Checkout page the customer visited.
Result Reference
The API returns all supported issues it finds. It does not return only the first issue. The list is not sorted as a public page order, therefore the list of issues does not correspond to Checkout pages in the same order.
Use valid First
Check result.valid first. Only inspect result.issues when validation fails.
const result = await Ventrata.validate({ orderId, apiKey, env: "live" });
if (result.valid) {
continueToPayment();
} else {
showMissingInformation(result.issues);
}π NOTE
continueToPayment() and showMissingInformation() are examples. This API does not add those functions.
An external implementation can follow its own flow and choose which issue to show first.
type OrderCompletionValidationResult =
| {
valid: true;
issues: [];
}
| {
valid: false;
issues: OrderCompletionIssue[];
};
type OrderCompletionIssue = {
reason: string;
step?: OrderCompletionStep;
bookingUuid?: string;
};
type OrderCompletionStep =
| "package-availability"
| "booking-questions"
| "contact-details"
| "pickup"
| "dropoff"
| "order-questions";
Types of validation results:
Complete order
Complete order
{
valid: true,
issues: []
}
The API did not find a supported completion problem in the saved order. The order can still expire, inventory can change, and the backend can still reject payment or confirmation for another reason.
Incomplete order
Incomplete order
{
valid: false,
issues: [
{
step: "booking-questions",
bookingUuid: "booking-uuid-123",
reason: 'Required booking question "question-id-123" is missing.'
}
]
}An issue can contain these fields:
Name | Description |
| The type of missing information |
| Top-level booking that needs attention, not always present |
| Human-readable detail for support and debugging |
More than one issue
More than one issue
{
valid: false,
issues: [
{
step: "booking-questions",
bookingUuid: "booking-uuid-123",
reason: 'Required booking question "question-id-123" is missing.'
},
{
step: "contact-details",
bookingUuid: "booking-uuid-123",
reason: 'Required unit contact field "phoneNumber" is missing.'
},
{
step: "order-questions",
reason: 'Required order question "question-id-456" is missing.'
}
]
}An order can have several missing values, which are returned together.
Every result contains the following:
validis always present.issuesis always present.When
validistrue, issues is always empty.When
validisfalse, the current implementation returns at least one issue.Every issue has
reason.A normal missing-information issue has
step.A booking-level issue also has
bookingUuid.An order-level issue has no
bookingUuid.An input, loading, or bad-data issue can contain only
reason.
Step Values
step identifies the category of missing information. It is intended to help your application decide how to recover from validation failures. It is not a Checkout route and cannot be used to open a Checkout page directly.
step identifies the category of missing information. It is intended to help your application decide how to recover from validation failures. It is not a Checkout route and cannot be used to open a Checkout page directly.
As a result, the public API does not include a function that opens a specific Checkout page. The external implementation must connect each value to its own recovery flow.
Step | Definition |
| A required availability still needs to be selected |
| A required booking, unit, or package booking question needs an answer |
| A required contact field still needs a value |
| A booking or package booking requires pickup selection |
| A booking or package booking requires dropoff selection |
| A required order-level question still needs an answer |
Current Issue Messages
These are all current missing-information message forms. Text inside <...> is replaced with a real UUID, question ID, or field name.
These are all current missing-information message forms. Text inside <...> is replaced with a real UUID, question ID, or field name.
βΌοΈ IMPORTANT
Use valid, step, and bookingUuid in your application logic. The reason field is intended for people and may change between releases.
|
|
|
|
| Parent booking |
|
| The referenced booking |
|
| Parent booking |
|
| The referenced booking |
|
| Parent booking |
|
| Parent booking |
|
| Parent booking |
|
| Parent booking |
|
| Parent booking |
|
| Parent booking |
|
| Parent booking |
|
| Not present |
|
| Not present |
What the API Does Not Do
The API does not:
Create an order
Update or fix an order
Submit or confirm payment
Guarantee that payment will succeed
Reserve inventory again
Tell the caller which screen was last open
Prove that a preselected pickup or dropoff was manually confirmed
Open Checkout on a returned step
Return an ordered list of pages
Replace final backend validation
Check every possible business rule
Check required consent checkboxes that only exist in local form state
Infer future save-as-quote intent from
quotesAllowedLoad general Checkout configuration such as
productID,multibooking, or feature flags
Consent checkboxes and other rules that are not stored on the order need a separate follow-up design. The validator cannot read data that is not in its input or in the loaded order.
Input, Loading, and Data Error Messages
The API returns an invalid result instead of leaving the caller with an uncaught error.
The API returns an invalid result instead of leaving the caller with an uncaught error.
This is a list of current non-completion error messages:
Scenario | Example |
No usable input |
|
|
|
|
|
The order is null |
|
The order has no bookings list |
|
The order has no usable ID |
|
Nested order data has a bad shape |
|
The order request or response fails |
|
π NOTE
For these errors, an issue may contain only reason, without step or bookingUuid. The promise still resolves with { valid: false, issues: [...] }, so you do not need to use try/catch for normal validation results.
A general try/catch is still good practice around the payment flow to handle unexpected runtime, network, or payment errors.
Troubleshooting
Ventrata.order is null or returns an invalid result "Order is missing."
Ventrata.order is null or returns an invalid result "Order is missing."
Some Checkout pages can appear before the first order is created. The exact pages depend on the product and flow. Pickup and dropoff can be part of this early flow.
Before the order exists:
Ventrata.ordercan benull;there is no
orderIdto load;Ventrata.validate({ order: Ventrata.order })returns an invalid result withOrder is missing.;the API cannot say how far the customer moved through those early pages.
This does not always mean the customer made an error. It can simply mean that there is no saved order to check yet.
An external implementation must keep its own page state before order creation.
The API becomes useful after the create-order request has succeeded and an order object or order ID exists.