Both sides must see the same state at once
The restaurant panel and the courier app run on separate devices; order status has to reach both through SignalR at the same time, with neither finding out late.
An order can land on the restaurant panel, or arrive separately from Trendyol, Yemeksepeti or Getir; deciding which courier carries which package is still a call the restaurant or an admin makes by hand. We built Kurjet as a system that brings these orders onto one panel, starts a courier's shift by location, and tracks delivery step by step.
Trendyol, Yemeksepeti and Getir orders reach the backend through separate queue consumers; orders entered on the restaurant panel join the same flow. The system doesn't pick a courier automatically — the restaurant or an admin assigns the package by hand from the panel. As order volume grows, seeing all three channels in one place and routing each to the right courier becomes the job itself.
The restaurant panel and the courier app run on separate devices; order status has to reach both through SignalR at the same time, with neither finding out late.
The courier app has to work while moving; on a weak signal, a location update can arrive late or get sent twice. The client filters out stale and duplicate locations to keep the display consistent.
When a courier wants to start a shift, they need to be near the restaurant; PostGIS calculates the distance, and outside the configured radius (500 meters by default) the shift won't start.
An order lands in the system from the restaurant panel or through the Trendyol, Yemeksepeti and Getir integrations via message queues; the kitchen marks it as being prepared, and the restaurant or an admin assigns the package to a courier by hand. The courier marks it picked up with one tap, then delivered; each step shows up on the restaurant panel instantly.
{ "id": "6f2b1e2a-••••-••••-••••-••••••••••••", "restaurantId": "9c4a7d10-••••-••••-••••-••••••••••••", "courierId": "3d8e5f21-••••-••••-••••-••••••••••••", "status": "OnTheWay", "requestFrom": "Trendyol", "address": "Örnek Mah. Örnek Sk. No:1, Kadıköy", "createdAt": "2026-03-14T15:22:00Z", "pickedUpAt": "2026-03-14T15:41:00Z"}{ "id": "6f2b1e2a-••••", "status": "OnTheWay", "requestFrom": "Trendyol", "pickedUpAt": "15:41" }
We put an 18-hour cap on shift length, then switched it off for now. Around the same time we took out a caching layer we had added.