Back to Case Studies
Travel / Accommodation Booking Cretico

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.

0.12 s Server response
368 Server-rendered pages
11 FAQ questions in JSON-LD

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:

Next.js 16React 19Payload 3PostgreSQLStripeTypeScriptBrevo

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

Metric Before After
Server response PHP 7.4, no framework 0.12 s
Page rendering dependent on JavaScript 368 server-rendered pages
Calendars per villa 1 as many as the owner has
Payment redirect to a bank page Stripe inside the page
Instant booking none live, per villa
Structured data minimal 15 types on every relevant page

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.

Next.jsPayload CMSStripePostgreSQLSEOGEOiCalAutomation
Live

See the result

The platform runs at creti.co, with the bookings, the payments and the automations described here.

Visit creti.co

Is 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.