Nortik

Case Study · Ruma Travel

AI booking, paidin loyalty points.

Ruma is a loyalty point AI booking platform for travellers who pay in points as often as in cash. We built the product end to end: a wallet holding more than thirty airline and hotel loyalty programs, a search that folds two travel data providers into one result set priced against those balances, the booking flow on top of both, and the administrative console the platform is run from.

Travel data providers integrated
2Travel data providers integrated
Loyalty and wallet programs
30+Loyalty and wallet programs
Flight and hotel results per search
800+Flight and hotel results per search

Engagement Overview

Ruma sells a simple promise to a traveller: show me every way to get there, and price it in whatever I actually have. Most people hold value in two currencies at once, money in an account and points scattered across airline and hotel programs they signed up for years apart, and almost no search will compare the two on the same screen.

We developed the travel platform that does. It aggregates flight and hotel offers from multiple providers into a single booking experience, and our work covered both sides of it: the customer-facing applications a traveller searches, filters and books in, and the administrative tooling the platform itself is run from. Frontend, backend and the infrastructure under both were ours.

The single biggest win is the aggregation itself. Two external providers, each with its own API, its own pricing model and its own response format, arrive as one ranked list a person can read top to bottom, with the cash fare and the points redemption sitting beside each other. The normalisation that makes that possible is the part of this engagement worth the most, and it is what the Solution below spends most of its length on.

2
Travel data providers integrated
30+
Loyalty and wallet programs
800+
Flight and hotel results per search
Engagement
End-to-End Product Development
Team
Full product team, design through infrastructure
Focus
Flight aggregation, booking and loyalty points management
Working model
Full product ownership, from infrastructure to frontend

Stack

Frontend

  • Next.js
  • React
  • TypeScript
  • Tailwind CSS
  • shadcn/ui
  • TanStack Query
  • Zustand
  • React Hook Form
  • Zod
  • i18next
  • TipTap

Backend

  • NestJS
  • TypeORM
  • JWT
  • Passport
  • Social sign-in
  • Handlebars

Data & storage

  • PostgreSQL
  • Redis
  • AWS S3

Infrastructure & DevOps

  • AWS Amplify
  • AWS SES
  • GitHub Actions
  • Jest
  • k6

AI & ML

  • OpenAI

Integrations

  • Kayak API
  • Odynn API
A fan of numbered cards laid out on a white surface, each one a different shade

The Challenge

The client wanted a modern travel platform capable of combining flight and hotel offers from multiple providers into a single user experience. That sounds like plumbing until you look at what arrives. Each external provider exposes a different API, prices differently and answers in its own response format, and none of them agrees with the others about what a fare even is.

So the offers cannot simply be listed side by side. They have to be normalised first: the same trip recognised across two feeds, the same baggage allowance read out of two different shapes, the same itinerary priced in cash on one and in points on the other, before anything can be ranked or compared. Get that wrong anywhere and a traveller sees an inconsistent list and stops trusting the whole screen.

None of that was the whole job either. The platform also needed authentication, a wallet that holds a person’s loyalty balances, localisation, content management and an administrative dashboard for the team running it, all while a single search kept processing hundreds of results fast enough that nobody watching it would go somewhere else.

The Solution

One internal model that every provider’s answer is translated into, and a search that prices the result in cash and in points at the same time. Everything a traveller touches is drawn out of that. Three stretches of it carried most of the weight.

Ruma flight results for Belgrade to New York, with Ruma Match, Cash Only and Points Only tabs above a recommended Air Serbia itinerary

Two providers, one comparable list

Flight search runs against the Kayak and Odynn APIs at the same time. Each answers in its own shape, so the work is in what happens next: the responses are normalised into one internal model, the same itinerary is recognised across both feeds rather than listed twice, and everything is ranked into a single list a person reads top to bottom. Above that list sit the three ways somebody actually wants to look at it. Ruma Match is the recommendation, the best of both currencies together, and Cash Only and Points Only are the same search answered as if the other currency did not exist. All three come from one query.

  • Kayak API
  • Odynn API
  • Response Normalisation
  • Cash and Points Pricing
The Ruma results screen with five active filters in the left rail, a recommended United Airlines itinerary priced in both dollars and points, and the Ruma Assistant panel open on the right

Filters that hold up at eight hundred results

A single search can process more than eight hundred flight and hotel results, and a list that long is only useful if it can be cut down without being reloaded. So the filter rail is wide and it is live: bags, stops, individual airports with their cheapest fare shown against each, take-off and landing windows on both legs, a price range, named carriers, and the loyalty providers a traveller actually holds. The result count updates as the rail moves, and the sort order can be flipped underneath all of it.

  • Advanced Filters
  • Sorting
  • TanStack Query
  • Live Result Counts
The Add Program dialog in the Ruma wallet, showing nine airline loyalty programs to choose from with a balance and membership number field beneath

A wallet that knows what the points are worth

Points only help if the search knows you have them. The wallet holds more than thirty airline and hotel loyalty programs, from Avios and Miles & More to TrueBlue, KrisFlyer and Aeroplan, each with a balance and an optional membership number. Once a program is in there it stops being a note in somebody's head and becomes part of the query: the results price against those balances, and an itinerary can come back as forty thousand points plus a few hundred in cash rather than as a fare a traveller then has to convert by hand.

  • Loyalty Wallet
  • 30+ Programs
  • User Accounts
  • Points and Cash Redemption

And everything holding the platform up

The parts a traveller only notices when they are missing, and the reason a search that fans out to two providers still comes back fast enough to keep somebody on the page.

  • Hotels on the same rails

    Hotel search and booking were built alongside the flight side rather than bolted on after it, so a stay goes through the same normalisation, the same filtering and the same checkout as a fare does.

  • An AI travel agent, not a filter panel

    An OpenAI-backed assistant sits beside the results, so a trip can be described in a sentence rather than assembled out of dropdowns. The filter rail stays there for anyone who would rather do it by hand.

  • Accounts and sign-in

    JWT sessions issued through Passport, with Google, Facebook and Apple sign-in in front of them, because a wallet is only worth filling in if it is still there on the next device.

  • An administrative control panel

    A console the platform is actually run from: the content on the customer-facing sites, the operational settings behind them, and the day to day work of keeping both current, without an engineer in the loop.

  • Localisation and content

    i18next across the interface, a TipTap rich-text editor for the copy that changes often, and Handlebars templates behind the transactional email going out through Amazon SES.

  • Shipped and load-tested

    Deployed on AWS Amplify with S3 behind it and GitHub Actions in front, Redis caching the responses that would otherwise be fetched twice, Jest on the units and k6 on the search paths that have to stay fast under load.

Nortik engineers around a table in the Novi Sad office, on a call with remote colleagues on the screen behind them

Business Impact

The platform delivers a unified travel booking experience by combining multiple providers into a single application. A traveller searches once and gets flights and hotels, in cash or against the loyalty balances they already hold, from an interface that never asks which feed an offer came out of.

What that normalisation bought is room to grow. Because every provider is translated into the same internal model, a new one is an adapter rather than a rewrite, and new features land on top of the existing search instead of beside it. The modular architecture allows both without major changes to the system, and the modern frontend and backend stack under it keeps the whole thing fast and scalable as more of each arrives.

View it from our angle.

Get In Touch
Bojan JovanovićNortik logo
Bojan Jovanović
Sr. Software Engineer, Nortik
Integrating multiple travel providers sounds straightforward until you discover that every API behaves differently. We spent considerable time normalizing data and handling edge cases so users would experience a seamless search instead of inconsistent results. That investment greatly simplified future feature development.

Nortik’s Impact

The Ruma travel platform end to end: flight and hotel search aggregated from two providers into one comparable result set, a wallet holding thirty-plus loyalty programs, the booking flow on top of both, and the administrative console the platform is run from.

Your AI team is ready.Are you?

Let's shape the future of AI, together.