Cretico, a booking platform rebuilt
Cretico has been selling private-pool holiday villas in Crete since 2014. Its platform was rebuilt from scratch: a new front end, a real booking system on Stripe, a back office for the staff, and an architecture that makes sure a page reads just as well to a visitor as it does to an AI engine.
The Challenge
The old platform was custom PHP 7.4 with no framework, written by hand and grown over a decade. It worked. Its limits, though, had stopped being technical and had become commercial.
Four things stood in the way of every booking:
- One inbound calendar per villa. An owner listed on two OTAs could not merge their availability, so the calendar drifted away from reality.
- No instant booking. Every stay was a request and a conversation, at a time when Airbnb and Booking close a deal inside a minute.
- Payment and cancellation terms applied globally, so no owner could set their own.
- A checkout that redirected to a bank page, which lost guests at exactly the last step.
On top of that, a decade of accumulated SEO spread across 609 URLs, none of which was allowed to break.
Search changed while it was being built
When the rebuild started, the question was how the page ranks on Google. By the time it finished, the question had become a different one: how it reads to ChatGPT, Perplexity and Google AI Overviews.
That changed two architectural decisions, and changed them early, while they were still cheap:
- AI crawlers do not execute JavaScript. Whatever is not in the HTML of the first response does not exist at all. So every page is rendered on the server, not in the browser.
- Structured data stopped being decoration for rich results. It became an ingestion path: Google now builds an accommodation listing by reading the page's own JSON-LD.
The Solution
Next.js 16 with Payload 3 over managed PostgreSQL. One application with two faces: the public site where the guest books, and the back office where the staff work every day. All on one managed server, with pm2 in front and Cloudflare for TLS.
The move off the old database was not a one-time job. It was an ETL that ran again every hour while the old platform kept taking bookings, with a check that counts every entity on both sides and stops the deploy if even one record is missing.
Built with:
The booking system
Three paths to a booking. What separates them is not the screen, it is what each one does to the money and what it does to the calendar.
Enquiry
Touches neither the card nor the calendar. It opens a conversation and nothing else.
Request to book
The default. The card is authorised but not charged, and the dates are held for 24 hours. Approval charges the deposit, a decline releases the authorisation immediately.
Instant book
Charged on confirmation, dates locked on the spot, on the villas whose owner allows it.
Underneath it, five rules that are never broken:
- The amount is always computed on the server. A price that arrives from the browser is a price the guest can edit.
- The Stripe webhook confirms the booking, not the browser. A guest who pays and closes the tab still has a booking.
- The Payment Element is embedded in the page. No redirect to a bank, anywhere.
- Deposit now, balance charged automatically later, with a reminder to the guest before the charge goes out.
- Refunds are computed by the code and carried out by a person. Nothing refunds itself.
What runs without anyone asking
Ten scheduled jobs on the server, each with a lock and a timeout so none of them can run over another.
Calendar sync, every 15 minutes
158 iCal feeds from Airbnb, VRBO and Booking. If even one of a villa's feeds does not come back clean, that villa does not move to the new system: an empty calendar does not mean a free villa.
Hold expiry, every 10 minutes
A request nobody answered within 24 hours releases itself. First the card is released at Stripe, then the booking is cancelled, then the guest is told. In that order.
Payment reconciliation, every 10 minutes
Catches payments the webhook never delivered. It only ever moves a booking forward, never back.
Balance charging, daily
A reminder four days ahead, the charge on the day the balance falls due. It charges the amount recorded at the time, never a fresh quote.
Morning digest to the office
It goes out only when something needs a person. Otherwise it does not go out at all, because a notification that arrives every day stops being read.
Watchdog, every 5 minutes
Restarts the application if pm2 does not report it online, and again when the machine boots.
Speed, SEO and visibility to AI engines
609 URLs, mapped one to one
Captured from the old platform's sitemaps before a line of code was written. Villa pages kept their numeric identifier inside the URL, so they did not change at all. An automated check proves the redirect map still holds.
15 schema types, from one source
VacationRental with AggregateOffer, GeoCoordinates, PostalAddress, amenityFeature and aggregateRating, plus BreadcrumbList, ItemList and FAQPage. All emitted from one file rather than scattered through templates.
The price cannot disagree with itself
The price a person reads and the price a machine reads come from the same field. The same goes for the property's registration number.
55 automated checks
Among them one that asserts a page and its structured data say the same thing, and one that walks the entire money path with a real test card.
Results
The speed and structured data figures were measured on the live creti.co on 17 September 2026. The rest of the column describes what changed architecturally, not a commercial outcome.
See the result
The platform runs at creti.co, with the bookings, the payments and the automations described here.
Visit creti.coIs your platform at its limits?
Let us work out what stays, what gets rewritten, and how the move happens without losing anything you have built.