Skip to main content

User Migration

Locate is moving its users and their points from the legacy Shukah system to its own services. This module documents, for the mobile app, what replaced each Shukah API the app calls in the user and points areas, field by field, and what is still missing.

Background​

Dated notes on how the migration got here. Facts about the services were read on 2026-10-01 and checked again by the parity audit of 2026-10-02; the list of what the app calls is from the app snapshot dated 2026-02-26.

  • 2026-06-25 to 2026-08-04, auth built. A bulk copy of users, addresses and tokens from Shukah into the auth service, then Shukah-style client routes under /api/v1/clients/* (moved from /maeda/api/* on 2026-07-10), then live sync in both directions.
  • 2026-07-06, the plan. Move all users and user points off Shukah onto the Locate (Merchant) platform, with both systems running in parallel so the move is gradual and can be rolled back. The plan estimated 3 to 4 weeks.
  • 2026-07-15 to 2026-09-22, points built. Ledger copy and live sync (2026-07-15), the read APIs (2026-07-16), then the point-order write path in slices (2026-09-17 to 2026-09-22).
  • 2026-10-01, what the app calls. The app snapshot calls the Shukah APIs listed on Migration progress. Most have a Locate route; a few do not, among them account deletion (GET /maeda/client/delete), which stays on Shukah. Please confirm the list against today's app.
  • 2026-10-01, the update popup. The app shows its "update the app" popup from four response headers (new_version, old_version, has_new_version, is_required_update). No Locate service sends them, so the popup would stop working after the switch, if Shukah sends them today (Not documented yet). See Verify token.
  • 2026-10-01, both point-order flags are on for dev. Paying a point order with points and with a card are each switched on by a server flag. Both answered as on for dev; production is not confirmed, and an off flag answers 403. See Point orders.
  • 2026-10-01, tokens. The app never calls exchange-token. A user holding a Shukah token who calls Locate verify-token gets 401, and the app then deletes the stored token and signs the user out. See Tokens.
  • 2026-10-01, only dev is documented. Hosts, captures and checks are for dev. Whether each Locate route is deployed to production is a separate question; most pages say so.
  • 2026-10-02, Shukah is being ported to Laravel. Dev Shukah answers with Laravel error bodies (type values such as Illuminate\… and Symfony\…). The "Implemented in Laravel" column in the team's production traffic sheet tracks that port, at the same URLs. It does not track the move to Locate.
  • 2026-10-02, the parity audit. The same input was sent to Shukah and to Locate on dev for every route that could be called safely. Main findings: no single token works on both services (the auth service's client routes, /api/v1/clients/*, take only the Locate JWT, the order-v2 point routes only the Shukah token, see Tokens); the address copy between Locate and Shukah is broken in both directions (see Address book); and Locate's point-order create answers without the order id the app keeps (confirmed by a live call later that day), so the pay calls cannot find the order (see Point orders). A follow-up call the same day found that points spent on one side are not taken off the other (within seconds of the run; whether it arrives later was not checked), so the app must read the balance where it creates orders. Balances on dev: on 2026-10-01 the same client had "514405.6788" points on Locate and "128712.41" on Shukah; on 2026-10-02 Shukah had "514853.64": the Shukah dev data had changed in between. Locate was then 447.96 points lower, which fits Locate's feed from Shukah lagging about two days (an inference; see Points wallet). The pages now carry these results, each with its date.

Who calls what​

Mobile app ──► Auth service ◄──────────► Shukah (legacy, /maeda/...)
│ login, token, profile, ▲ still serves delete-account,
│ address book │ restaurant orders, payments
│ │
└──────► order-v2 (/api/v2/client) ─────┤ order-v2 also calls Shukah
│ passes the point routes │ for some point calls
▼ │
Point service ◄────────────────┘ ledger sync (designed both ways; see Point orders)
(not exposed to the app)

The services also call each other: order-v2 and the point service ask the auth service GET /api/v1/users who the caller is, so the app does not send a header for that. The details of the syncs are on the pages under Details (for backend).

AreaThe app callsServed byShukah before
Login and registrationreset-otp, verify-otpAuth serviceGET /maeda/api/reset-otp, GET /maeda/api/verify-otp
Session and profileverify-token, client lookup, update profile, push setting, add deviceAuth service/maeda/api/verify-token, /maeda/client, PUT /maeda/client/{id}, /maeda/client/add-device-id
Address booklist, get, create, update, deleteAuth service/maeda/address-book and /maeda/address-book/{id}
Token exchangeexchange-token (the app does not call it yet)Auth serviceNothing: a Locate-only endpoint
Pointsbalance, history, FAQ, offers, occasionsorder-v2, which passes them to the point service/maeda/client/points, /maeda/client/point-history, /maeda/points-faq, /maeda/point-offer, /maeda/point-occasion
Point orderscreate, read, description, pay by card or sessionorder-v2 and the point service/maeda/point-order and the checkout-payment routes
Earn rates, gift-card codepoint rates, gift-card codeorder-v2 itself/maeda/point-rate, /maeda/giftcard-code/use
Delete accountNot replacedStays on ShukahGET /maeda/client/delete

Migration progress has the full list with each route's status, and Shukah vs Locate sets each Shukah API against its replacement.

Base URLs​

The app builds a URL from a base URL, a / and the endpoint. The Locate pages write curl examples with $AUTH_BASE_URL, $ORDER_BASE_URL and $SHUKAH_BASE_URL, which are the host only. The prefix goes in the path: /api/v1 on the auth host, /api/v2/client on the order-v2 host and /maeda on Shukah.

Dev​

ServiceHostPrefix
Auth servicehttps://dev.authentication.locate.sa/api/v1
order-v2https://dev.v2.api.order.locate.sa/api/v2/client
Point servicehttps://dev-point-api.locate.sa (the app does not call it)/api/client
Shukahhttps://dev-api.shukah.com/maeda

UAT​

ServiceHostPrefix
Auth serviceNot documented yet — ask backendNot documented yet — ask backend
order-v2https://dev.v2.api.order.locate.sa (the same as dev in the app's UAT settings)/api/v2/client
Point serviceNot documented yet — ask backendNot documented yet — ask backend
Shukahhttps://dev-api.shukah.com (the same as dev in the app's UAT settings)/maeda

Production​

ServiceHostPrefix
Auth serviceNot documented yet — ask backendNot documented yet — ask backend
order-v2https://v2.api.order.locate.sa/api/v2/client
Point serviceNot documented yet — ask backendNot documented yet — ask backend
Shukahhttps://api.shukah.com/maeda

The app has no base URL for the auth service in any of its environment settings today, so it needs one. The hosts above come from the app's own settings and the services' docs; the auth service's UAT and production hosts are in deployment settings that research did not see.

Tokens​

  • Shukah token. An opaque string. Shukah issues it at login.
  • Locate token. A JWT that the auth service issues. It has no expiry and nothing revokes it.

Both go in Authorization: Bearer <token>. Which pages take which:

PagesToken
OTP login, Exchange token, and the add-device-id call on Profile and settingsNone needed.
Verify token, Client lookup, Address book, and update profile and update setting on Profile and settingsA Locate JWT only. A Shukah token gets 401.
The points pages (Points wallet, Point history, Point offers, Point orders, Points FAQ)A Shukah token only, on dev today. A Locate JWT gets 401 Invalid or expired token (captured on dev, 2026-10-02; on 2026-10-01 it still got 200). The earn-rate route is public. A page says when a route needs no token.
User detailWith x-auth-from: SHUKAH, dev passes the token on to Shukah, so a Shukah token works and a Locate JWT is expected to get 401 (inference, 2026-10-02: the point routes that call it refused the JWT; this route was not called with x-auth-from that day). The points routes depend on this call, which is why a Locate JWT fails there.

So the app needs both tokens today: the Locate JWT for the auth service and the Shukah token for order-v2. Shukah vs Locate explains why, and why this matters when the app switches.

Getting a test token​

The auth pages' Try it needs a Locate JWT in $TOKEN, except User detail, which needs a Shukah token. The order-v2 points pages' Try it needs a Shukah token in $SHUKAH_TOKEN today, because a Locate JWT gets 401 there (dev, 2026-10-02). Use dev only. There are two ways to get a Locate JWT (A and B below), and one way to get a Shukah token from it (C).

A. Log in with an OTP​

Two calls: reset-otp asks for the code, verify-otp trades the code for a token.

export AUTH_BASE_URL=https://dev.authentication.locate.sa
export PHONE='<test-mode-phone>' # placeholder: replace it with a test-mode number
curl -s -G "$AUTH_BASE_URL/api/v1/clients/reset-otp" --data-urlencode "contact_phone=$PHONE"
curl -s -G "$AUTH_BASE_URL/api/v1/clients/verify-otp" --data-urlencode "contact_phone=$PHONE" --data-urlencode "code=2022"

The second answer has the token in access_token. Keep it as $TOKEN.

warning

reset-otp sends a real SMS unless the account is a test-mode account (account_mode: TEST). A test-mode account gets the fixed code 2022 and no SMS. The service has no API to make an account test-mode: backend sets it in the database. Ask backend for a test-mode number; whether one exists on dev: Not documented yet.

The hard-coded number +966500000000 also accepts 2022, but the service still tries to send an SMS to it, so do not use it. Never put a real person's number in a test.

Notes: a code lasts 3 minutes, and while one is still valid reset-otp sends nothing new (it still answers {"status":true}). Backend can read the latest OTP through an admin debug route that is protected by an API key; it is for backend only, so ask backend. See OTP login for the full rules.

B. Exchange an existing Shukah token​

If you already hold a Shukah token for a dev client, trade it for a Locate JWT. A Shukah token comes from the existing Shukah login; how to get one without the app: Not documented yet.

export AUTH_BASE_URL=https://dev.authentication.locate.sa
export SHUKAH_TOKEN='<shukah-token>'
curl -s -X POST "$AUTH_BASE_URL/api/v1/clients/exchange-token" -H "Content-Type: application/json" -d "{\"token\":\"$SHUKAH_TOKEN\",\"target_platform\":\"locate\"}"

The Locate JWT is in data.token. Keep it as $TOKEN.

This call writes: each time it stores a new token for the client and pushes it to Shukah. Do not send two for the same client in the same second. See Exchange token.

C. Get a Shukah token from a Locate JWT​

The same exchange-token call with your Locate JWT and target_platform set to shukah returns the Shukah token stored for that client: curl -s -X POST "$AUTH_BASE_URL/api/v1/clients/exchange-token" -H "Content-Type: application/json" -d "{\"token\":\"$TOKEN\",\"target_platform\":\"shukah\"}".

The Shukah token is in data.token. Keep it as $SHUKAH_TOKEN. The JWT is checked first. The answer can be {"status":200,"data":{}} when the stored Shukah token has no raw copy; then this way does not work for that client (see Exchange token).

The variables the pages use​

export AUTH_BASE_URL=https://dev.authentication.locate.sa
export ORDER_BASE_URL=https://dev.v2.api.order.locate.sa
export SHUKAH_BASE_URL=https://dev-api.shukah.com
export TOKEN='<locate-jwt>'
export SHUKAH_TOKEN='<shukah-token>'

Replace the two token placeholders with your own. Do not commit a real token to a file or paste it into a ticket or a chat.

How to read these pages​

An endpoint page has this shape:

  1. Background and Feature: what the app does today on Shukah, and what the feature is, in plain words.
  2. One block per endpoint, in this order:
    • the TL;DR box (headed "Replaces Shukah"): the Shukah API it replaces and its status; the verdict with the number of breaking and to-watch differences; one line per difference, 🔴 for breaking and 🟡 for to watch; the service that serves it, which tokens worked, how and when it was verified, and a link to the Shukah side. The Migration progress legend explains the verdicts and marks;
    • Request and Response: tables of every field;
    • Errors: the real bodies;
    • Try it: a curl using the variables above;
    • Differences from Shukah: only real differences, each with its impact on the app. A route with none says Drop-in — no differences found, with the date.
  3. Watch out: at most three one-line warnings.
  4. A line, then Details (for backend): business rules, hazards and the Sources, which name the repository, commit and file for each fact.

The TL;DR box and the progress table use these statuses:

MarkMeaning
✅ ReplacedA Locate route exists.
❌ Not replacedNo Locate route exists yet.
➖ Not migratingThe API stays on Shukah on purpose.
🚩 flagImplemented, but a switch (a server flag or an app setting) decides whether it is used.

Migration progress adds a Dev check: one request to the Locate route on dev without a token, on the date shown. It shows that the route is deployed and answers, not that it works for a signed-in client.

Not documented yet means no code, doc or capture showed the answer. It is not a guess and not "no". Treat it as a question for the backend or mobile team.

Samples say where they come from: captured on dev on a date (personal data replaced with illustrative values), or taken from a service's doc, with its path and date. Capture file names under Sources refer to the uncommitted 2026-10-01 dev capture set and the 2026-10-02 parity-audit capture set (names like points--shukah), which the docs author keeps; they are not in this repository.

An exception to the other modules. Most modules never restate field names or response shapes, because the service's rendered OpenAPI is the source of truth. The services here publish no OpenAPI, so these pages document the fields themselves, from the code. A field table can therefore go stale: check the commit named under Sources, and when a page and the service disagree, the service wins.

Tags​

Pages carry tags. Each links to every page that has it.

TagMeaning
auth-serviceServed by the auth service. The app calls it directly.
order-v2Reached through order-v2's client API.
point-serviceServed by the point service, behind order-v2.
authenticationLogging in, tokens and sessions.
profileThe client's profile, settings and devices.
address-bookThe client's saved delivery addresses.
pointsBalance, history, offers, occasions and FAQ.
point-ordersBuying or gifting points, and paying for them.
shukah-replacementA Locate endpoint that replaces a Shukah API.
new-endpointA Locate endpoint with no Shukah equivalent.
overviewModule-level pages: background, progress and the comparison.
mobileWritten for the mobile app team.
backendCalled by backend services, or written for backend engineers.
clientCalled by the client app with the client's own token.
adminCalled from an admin tool, not by the client app. No page has this tag yet.
server-to-serverCalled by another backend service, not by an app.

Status is not a tag. It lives in Migration progress.

Pages​

Start here if you are switching the app: the Switch checklist.

Auth service​

  • OTP login: log in or register with a phone number and a 4-digit code.
  • Verify token: check the token and get the signed-in client's profile.
  • Exchange token: trade a Shukah token for a Locate token, or the other way.
  • User detail: resolve a token into a profile; the route order-v2 and the point service call.
  • Client lookup: find a client by phone number.
  • Address book: list, read, create, edit and delete saved addresses.
  • Profile and settings: update the profile, switch push on or off, register a device.

Points​

  • Points wallet: balance and expiry, earn rates, gift-card code.
  • Point history: the client's point transactions.
  • Point offers: the packages a client can buy or gift, and the gift occasions.
  • Point orders: create, read and pay a point gift order.
  • Points FAQ: the questions and answers in English and Arabic.