Skip to main content

Shukah vs Locate

Overview​

No Shukah API documentation exists anywhere. This page is the only place the Shukah side is written down: what the mobile app sends to each Shukah route, what comes back, and how the Locate route that replaces it differs. Every "Compare with Shukah" link on an endpoint page lands on a section here.

How to use it:

  • Start with the two tables below. Click a Shukah route to jump to its section.
  • Read Cross-cutting differences once. Base URLs, tokens, error bodies, ids, dates and headers change the same way on every route, so the sections below do not repeat them.
  • A section has the full dev URL, the request the app sends, a curl (left out for routes that delete an account or take a payment), the captured Shukah response, the errors seen, and, for a replaced route, a link to the Locate page and a field-by-field table.
  • Shukah captures were taken on dev on 2026-10-01 with the dev test client 356259. On 2026-10-02 a parity audit sent the same input to Shukah and to Locate for every route it could call safely, with the app 2.31.5 headers; a section says when a value comes from that run. Personal fields in every sample are replaced with illustrative values. A value that no source shows is marked "Not documented yet". Where the app's own expectation is the only Shukah source, the section says so.
  • $SHUKAH_BASE_URL is https://dev-api.shukah.com and $SHUKAH_TOKEN is a Shukah opaque token. The Locate pages use $AUTH_BASE_URL, $ORDER_BASE_URL and $TOKEN.
  • Two Shukah entries, update profile and update setting, are the same Shukah route. They are two rows because Locate splits them into two routes.

Auth​

Shukah APILocate APIServiceStatus
GET /maeda/api/reset-otpGET /api/v1/clients/reset-otpauth-service✅ Replaced
GET /maeda/api/verify-otpGET /api/v1/clients/verify-otpauth-service✅ Replaced
GET /maeda/api/verify-tokenGET /api/v1/clients/verify-tokenauth-service✅ Replaced
GET /maeda/clientGET /api/v1/clientsauth-service✅ Replaced
GET /maeda/address-bookGET /api/v1/clients/address-bookauth-service✅ Replaced
GET /maeda/address-book/:idGET /api/v1/clients/address-book/:idauth-service✅ Replaced
POST /maeda/address-bookPOST /api/v1/clients/address-bookauth-service✅ Replaced
PUT /maeda/address-book/:idPUT /api/v1/clients/address-book/:idauth-service✅ Replaced
DELETE /maeda/address-book/:idDELETE /api/v1/clients/address-book/:idauth-service✅ Replaced
PUT /maeda/client/:client_idPUT /api/v1/clients/:client_idauth-service✅ Replaced
PUT /maeda/client/:client_idPUT /api/v1/clients/settingauth-service✅ Replaced
POST /maeda/client/add-device-idPOST /api/v1/clients/add-device-idauth-service✅ Replaced
GET /maeda/client/status——❌ Not replaced
GET /maeda/client/delete——❌ Not replaced

Points​

Shukah APILocate APIServiceStatus
GET /maeda/client/pointsGET /api/v2/client/pointsorder-v2 → point-service✅ Replaced
GET /maeda/point-rateGET /api/v2/client/system_configs/point_ratesorder-v2✅ Replaced 🚩 enableMerchantService
GET /maeda/giftcard-code/usePOST /api/v2/client/gift-card-codeorder-v2✅ Replaced 🚩 enableMerchantService
GET /maeda/client/point-historyGET /api/v2/client/point-historyorder-v2 → point-service✅ Replaced
GET /maeda/point-offerGET /api/v2/client/point-offersorder-v2 → point-service✅ Replaced
GET /maeda/point-occasionGET /api/v2/client/point-occasionsorder-v2 → point-service✅ Replaced
POST /maeda/point-orderPOST /api/v2/client/point-ordersorder-v2 → point-service✅ Replaced 🚩 POINT_ORDER_POINT_ENABLED
POST /maeda/point-order/get-descriptionPOST /api/v2/client/point-orders/descriptionorder-v2 → point-service✅ Replaced
POST /maeda/order/checkout-payment-standalonePOST /api/v2/client/point-orders/:correlation_id/pay/cardorder-v2 → point-service✅ Replaced 🚩 POINT_ORDER_CARD_ENABLED
POST /maeda/order/checkout-payment-flowPOST /api/v2/client/point-orders/:correlation_id/pay/sessionorder-v2 → point-service✅ Replaced 🚩 POINT_ORDER_CARD_ENABLED
GET /maeda/points-faqGET /api/v2/client/points-faqorder-v2 → point-service✅ Replaced
GET /maeda/point-order——❌ Not replaced
POST /maeda/order/checkout-payment——❌ Not replaced
POST /maeda/order/checkout-payment-stcpay——❌ Not replaced
POST /maeda/order/payfort-token——❌ Not replaced
POST /maeda/order/payfort-stc-pay-checkout——❌ Not replaced

Where the docs and the code disagree​

Claims of earlier versions of this page that the 2026-10-02 captures or the code contradict. Each section now says what was found.

  • 2026-10-02: Tokens said a Locate JWT worked on the order-v2 points routes. On dev it now gets 401; only the Shukah token works there.
  • 2026-10-02: IDs and the three payment sections said the app sends point_order_id as an integer. App 2.31.5 sends a string ("1252", "1253").
  • 2026-10-02: Number formats and the balance section gave the Shukah balance as "128712.41", about a quarter of Locate's. It is now "514853.64", 447.96 points above Locate.
  • 2026-10-02: reset-otp, update profile, update setting, add-device-id and the sent-gifts list had no Shukah capture. They have one now.
  • 2026-10-02: verify-otp gave the wrong-code body with a Yii type. Today's Shukah body has a Symfony type.
  • 2026-10-02: the verify-otp section had no Shukah capture of a successful call. It has one now: 200 {access_token, data}, the same 13-key profile with equal values as Locate, and earlier sessions stay valid.
  • 2026-10-02: the address list gave 13 and 28 addresses, no default on Shukah and all default on Locate, and an unknown Shukah order. Now 15 and 30, Shukah 0 on all 15, Locate true on 28 of 30, and Shukah orders by position.
  • 2026-10-02: the address list showed no image_urls difference. Locate lost the photo URLs of the 4 Shukah addresses that have one.
  • 2026-10-02: the address get section showed no is_default or image_urls difference. The same address is 0 on Shukah and 1 on Locate, and has a photo URL on Shukah and [] on Locate.
  • 2026-10-02: the point history section said Shukah's newest earn amounts (0.5, 0.5, 2.5) were on a different scale from Locate's. Shukah's newest rows are now 2, 2, 2, 2, 10: no scale gap. Shukah's newest row is 2026-10-01 17:00:01, not 10:40:44, and 8 of the 9 Locate earn rows among the first 10 have an empty description, not four of five.
  • 2026-10-02: the point-order create and sent-gifts list sections expected occasion_id. Shukah sends point_occasion_id.
  • 2026-10-02: the point-order create section had no Shukah capture and did not know whether create answers with the order row. It has one now: 201 with the same twelve keys as the order rows.
  • 2026-10-02: the PayFort token section named no Locate call. order-v2 has a relay to the same Shukah route.
  • 2026-10-02: GET /maeda/client/status was listed as declared but never called. Production traffic shows 768 calls in 3 days, so it has a section now.

Cross-cutting differences​

Base URLs​

ShukahLocate
Auth APIs (login, profile, address book)Dev https://dev-api.shukah.com/maeda/, production https://api.shukah.com/maeda/ (the app's BASE_URL).The auth service, called directly. Dev https://dev.authentication.locate.sa, prefix /api/v1. Production host: Not documented yet.
Points APIsSame Shukah base URL.order-v2. Dev https://dev.v2.api.order.locate.sa, prefix /api/v2/client. Production https://v2.api.order.locate.sa/api/v2 (the app's production BASE_URL_V2). order-v2 passes most point routes to the point service, which is not exposed to the app.

What changes for the app:

  • Two prefixes are new, one is gone. Every Shukah route sits under /maeda. The Locate auth host does not serve it: GET /maeda/api/verify-token on the auth host answers 404 (captured on dev, 2026-10-01). Auth routes move to /api/v1/clients/…, point routes to /api/v2/client/….
  • The api/ segment is not stable on Shukah. Login routes have it (/maeda/api/reset-otp); the address book does not (/maeda/address-book). A call to /maeda/api/address-book on Shukah dev answers 404 (captured 2026-10-01).
  • Point paths become plural. point-offer becomes point-offers, point-occasion becomes point-occasions and point-order becomes point-orders.
  • The app has no auth-service base URL. No app environment file names the auth host. It does have an order-v2 base URL (BASE_URL_V2), which it already uses for a few Merchant-mode calls.
  • URL building. The app joins the base URL, / and the endpoint, then a query sorted by key. A leading slash on the endpoint makes no difference.

Tokens​

Shukah issues an opaque token. Locate issues a JWT (HS256, payload {client_id, phone}) that never expires. Both go in Authorization: Bearer <token>.

No single token works on both services today. Which Locate route takes which token (dev, captured on 2026-10-02 with both tokens on every route that could be called safely):

Locate routesToken
Auth: reset-otp, verify-otp, exchange-token, add-device-idNone needed.
Auth: verify-token, client lookup, the five address-book routes, update profile, update settingLocate JWT only. A Shukah token gets 401 {"status":401,"message":"Unauthorization"} (captured).
Auth: user detail (GET /api/v1/users)Needs an x-auth-from header (422 without it). With x-auth-from: SHUKAH and dev's auth_source of shukah, the token is checked by dev Shukah: a Shukah token passes and a Locate JWT is refused. With auth_source set to locate, every Locate JWT gets 401 (a defect).
order-v2 points routes (balance, history, FAQ, offers, occasions, point orders)Shukah token only. A Locate JWT gets 401 {"success":false,"error":{"code":"UNAUTHORIZED","message":"Invalid or expired token"}} on the balance, history, FAQ, offers and occasions routes (captured). The point-order routes use the same check. On 2026-10-01 a Locate JWT still got 200 on the balance route.
order-v2 earn ratesNone needed: public. 200 with no token, with a Shukah token and with a Locate JWT (captured).
order-v2 gift-card codeThe same check as the point routes, so a Shukah token only (code reading; not called with a token, because it credits points).

Why (from the code on origin/develop): the auth service's client guard runs jwt.verify, so an opaque Shukah token fails it. The point service and order-v2 ask the auth service who the caller is with x-auth-from: SHUKAH: order-v2's guard hard-codes it, and the point service sends it by default (its AUTH_VERIFY_APPLICATION setting). Dev's auth_source setting is shukah (read from the public client settings on 2026-10-02), so the auth service passes the token on to dev Shukah, which refuses the Locate JWT (an inference from the 401: dev Shukah's verify-token was not called with the JWT itself). On 2026-10-01 Shukah still accepted it. Setting auth_source to locate would not help on its own: in that branch every Locate JWT gets 401, because of a defect described on User detail. So the app needs both tokens today: the JWT for the auth service, the Shukah token for order-v2.

Shukah itself answers four routes with no token: point-offer, point-occasion, point-rate and points-faq. Locate needs a token for offers, occasions and the FAQ.

Exchange-token converts a token in either direction (target_platform is locate or shukah). The app never calls it. So a user who is signed in with a Shukah token and calls Locate verify-token gets 401, and the app deletes its stored token on that 401 (only verify-token and delete-account do this), which signs the user out. The routes that stay on Shukah (delete account, the sent-gifts list, the PayFort and Frames payments) still need a token Shukah accepts: Locate pushes each JWT it issues to Shukah on a best-effort basis (behind a server setting), or the app can exchange for a Shukah token with target_platform: "shukah". On 2026-10-02 dev Shukah no longer accepted the Locate JWT (see above), so the push cannot be relied on there.

Error bodies​

Five error shapes meet in this migration. One real body of each:

Sent byReal bodyWhere it was captured
Shukah (Laravel, today's probes){"name":"Unauthorized","message":"Your request was made with invalid credentials.","code":0,"status":401,"type":"Illuminate\\Auth\\AuthenticationException"}GET /maeda/api/verify-token with no token, dev, 2026-10-01. The same name, code, status and type keys carry a 404 ({"name":"Not Found","message":"Object not found: 162119",…,"type":"Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException"}, address get after delete, 2026-10-01).
Shukah (Yii, older capture){"name":"Bad Request","message":"Last Name cannot be blank.","code":0,"status":400,"type":"yii\\web\\BadRequestHttpException"}From a call to Shukah reset-otp, in the July 2026 capture kept in the auth service's draft doc (api-auth-draft.md, 2026-07-07). Whether Shukah still sends it: Not documented yet. The auth service's own reset-otp and client-lookup 400 use this Yii shape too.
Auth service{"status":401,"message":"Unauthorization"}GET /api/v1/clients/verify-token, no token, dev, 2026-10-01. 500 is {"status":500,"message":"Internal server error"} in production. On dev it carries the raw error text and a stack field.
Point service (through order-v2){"success":false,"error":{"code":"UNAUTHORIZED","message":"Missing or invalid authorization header"}}GET /api/v2/client/points, no token, dev, 2026-10-01. A details key is added for validation errors. order-v2 builds 502 and 504 bodies in the same shape.
order-v2 itself (Nest){"message":"Unauthorized","statusCode":401}POST /api/v2/client/gift-card-code, no token, dev, 2026-10-01.

How the app surfaces them: a status of 400 or more becomes an error whose message is body.message, or the whole body as a JSON string when there is no top-level message. So:

  • The Shukah, auth-service and Nest bodies show their message. The name, type, code and statusCode keys are never read.
  • The point-service envelope has no top-level message, so the app would show the raw JSON string {"success":false,"error":{…}} to the user unless it reads error.message itself.
  • A body that is not JSON (an HTML 404 page) becomes {message: <text>}.
  • The app opens a global error for 403, 500, 502, 503, 504 and 505 by default. The point-order flags answer 403, so that is a critical error unless the status list is changed.
  • The registration cue still works if the text stays the same: the app opens the registration screen when reset-otp answers 400 and the message contains Last Name.

IDs​

ThingShukahLocate
ClientInteger client_id (356259).The same integer for a migrated client. Clients registered in Locate get ids from 1,000,000,001.
AddressInteger id (162115).24-character hex string (6abbeed8a492ed4862500b74). A value made only of digits in the path is still tried as the old Shukah id first.
Point offer, point occasionInteger id, 1 to 5 and 1 to 7.24-character hex string. A Shukah integer sent to Locate is 400.
Point orderpoint_order_id, a number in Shukah's order list. The app 2.31.5 payment bodies send it as a string, "1252" (captured on dev, 2026-10-02).UUID correlation_id, in the path of the get and pay routes. A point_order_id in the body is ignored. order_no (from 100000) is the readable number.
Point-history rowInteger id on expense rows, shukah_id on earn rows.24-character hex id on expense rows. shukah_id is null for rows Locate wrote itself.

The app types every id as a number. Its delete-address call turns the id into a number with a unary plus, so a 24-hex id becomes NaN. Offer and occasion ids reach Locate as text and work.

Pagination​

CallShukahLocate
Point historyNone: the whole history in one array (8,134 rows for client 356259 on 2026-10-01, 2.3 s; 8,136 on 2026-10-02).Query page and limit. Default 20, maximum 100. No total and no next-page marker. The app sends neither.
Sent-gifts list (GET point-order)Query page and per-page. Totals in the response headers x-pagination-total-count and x-pagination-page-count.No route.
Address list, offers, occasions, FAQ, ratesNone.None.

Dates and time zones​

Both sides use Riyadh wall-clock time (Asia/Riyadh, UTC+3, no daylight saving) for ledger rows, and no zone marker.

ValueShukahLocate
Point-history date_createdYYYY-MM-DD HH:mm:ss, Riyadh time.The same format and zone.
pointsExpirationDateYYYY-MM-DD.YYYY-MM-DD, a date in Riyadh.
birth_dateYYYY-MM-DD.YYYY-MM-DD: the UTC date of what was written.
Gift-card result date_createdNot documented yet.yyyy-mm-dd hh:mm:ss, Arab time (UTC+3).
Address created_at, updated_atNot returned.ISO 8601 in UTC (2026-09-29T17:01:10.000Z), on the list and create answers only.

Number formats​

ValueShukahLocate
Point balance pointsString with two decimals: "514853.64" (2026-10-02).String with up to four decimals, trailing zeros removed: "514405.6788". "0" when there is no account. The app converts with Number(points) || 0.
History total_pointsA number on earn rows, a string with two decimals on expense rows ("684.00", measured by the point service team on 2026-07-17).A number on earn rows and a string with exactly two decimals on expense rows ("360.00").
Offer amount, order money_amountamount is a string with two decimals: "40.00".amount the same; the order's money_amount is a string with two decimals.
Offer point_amount, purchased_point_amountIntegers.Numbers. purchased_point_amount can be null when the offer has no points price.
Address lat, lngJSON numbers on every route.Numbers on list and create, strings on get and update.
Address is_default0 or 1 in the app's type. 0 on every address and every route in the 2026-10-02 captures.Boolean on list and create, 0 or 1 on get and update (confirmed by the code and the 2026-10-02 captures).
sex, receive_offers, push_enabledIntegers (sex 0, 1, 2 or 9).Integers. sex 9 is stored as unknown and read back as 0.

Request headers​

Request. The app sends these on every Shukah call (and on order-v2 calls):

Header or queryValueLocate
AuthorizationBearer <stored token> when signed in.Needed on guarded routes. See Tokens.
Accept, Content-Typeapplication/json.Content-Type: application/json is needed on the order-v2 POST routes that read a body.
x-from-applicationLOCATE_CLIENT_V2.Ignored by the auth service. order-v2 does not pass it on to the point service.
from-application, x-from-versionThe app mode and the app version.The same: ignored or not passed on.
language (query)en-GB, or ar-SA for any other language.The auth service ignores it, except reset-otp, which stores the part before -. The point routes ignore it, and the occasion label is Arabic only when language is exactly ar.
key (query)maeda, sent only on reset-otp and the phone lookup.Ignored.
x-auth-fromSHUKAH, sent only by the app's shukahApi helper, not by the main client.The point service sets its own value when it calls the auth service. The app does not need to send it.

The 2026-10-01 captures sent only Accept, plus x-from-application and Content-Type on the writes, with the token, and got 200. The 2026-10-02 captures sent the app 2.31.5 headers (x-from-application, from-application, x-from-version: 2.31.5, x-user-id, Accept) and ?language=en-GB on both sides. x-device-id is not sent by the app.

Response: app-version headers. The app reads new_version, old_version, has_new_version and is_required_update from the response of any call whose URL contains api/verify-token or client/points, and shows its update popup from them. Whether Shukah sends them: Not documented yet (the captures store request headers only). No Locate service sends them. The Locate verify-token URL (/api/v1/clients/verify-token) does not contain api/verify-token, so the app would not look there. The Locate points URL does contain client/points, but nothing sets the headers. If Shukah sends them today, the update popup silently stops after the switch. See Verify token.

Response: pagination headers. The sent-gifts list reads x-pagination-total-count and x-pagination-page-count. See Pagination.

Auth APIs​

GET /maeda/api/reset-otp​

Locate replacement: GET /api/v1/clients/reset-otp

Full dev URL: https://dev-api.shukah.com/maeda/api/reset-otp

One route does both sign-in and registration. No Authorization: the user is signed out.

Request (from the app). Sign-in sends contact_phone (digits only, + removed), key=maeda and language. Registration adds first_name, last_name, email_address and, optionally, sex (0, 1, 2 or 9), birth_date (yyyy-mm-dd) and receive_offers (sent as 1 only when the user opts in, never 0).

# Sends a real SMS to the phone. Use a test-mode number.
curl -s -G "$SHUKAH_BASE_URL/maeda/api/reset-otp" --data-urlencode "contact_phone=$PHONE" --data-urlencode "key=maeda" --data-urlencode "language=en-GB"

Captured Shukah response. 200, 895 ms, for a known phone (captured on dev, 2026-10-02; it sent one SMS). Locate answered the same body. The app's parsed type is {status: any}. The auth service's draft doc (api-auth-draft.md, 2026-07-07), from a call to dev Shukah, shows the same {"status": true} for a registration.

{
"status": true
}

Errors seen.

  • Missing phone, 400, in Arabic and with a Laravel type (captured on dev, 2026-10-01, with key=maeda and no language): {"name":"Bad Request","message":"حقل contact phone مطلوب.","code":0,"status":400,"type":"Illuminate\\Validation\\ValidationException"}
  • Unknown phone with no registration fields, 400, in the July 2026 capture kept in the auth service's draft doc (api-auth-draft.md, 2026-07-07): {"name":"Bad Request","message":"Last Name cannot be blank.","code":0,"status":400,"type":"yii\\web\\BadRequestHttpException"}. The app's cue to open registration. Whether Shukah still sends this text today: Not documented yet.
FieldShukahLocateNote
contact_phone (query)Digits only, + removed by the app.Whitespace removed, matched with or without a leading +. No format check.Blank gives 400 on both.
key (query)maeda.Ignored.The app can keep sending it.
first_name, last_name, email_address (query)Registration fields.Needed when no active client has the phone. Checked in the order last name, first name, email.A bare call for an unknown phone gets Last Name cannot be blank. on Locate. Shukah's current text: Not documented yet.
sex (query)0, 1, 2 or 9.1 male, 2 female. Anything else is stored as unknown.
birth_date (query)yyyy-mm-dd.Read as a date, stored as YYYY-MM-DD. A bad value is silently stored as empty.
receive_offers (query)Sent as 1 only.Not read. A new client starts with it off.
language (query)en-GB or ar-SA.Stored as the part before -, lower case. Missing becomes ar.Does not change the SMS language (Arabic).
status (response)true.true. Also true when a code is still pending and no new SMS was sent.Do not read true as "SMS sent".
Missing-phone errorArabic message, Laravel type.{"name":"Bad Request","message":"Phone number cannot be blank.","code":0,"status":400,"type":"yii\\web\\BadRequestHttpException"} (captured on dev, 2026-10-01).The keys are the same, the text and type differ.

GET /maeda/api/verify-otp​

Locate replacement: GET /api/v1/clients/verify-otp

Full dev URL: https://dev-api.shukah.com/maeda/api/verify-otp

Request (from the app). Query contact_phone, code (4 digits), fcm_token (left out when the app cannot get one), platform (Android, iOS or Huawei) and language. No key. No Authorization.

# Uses up the code and returns a new token. Use a test-mode number.
curl -s -G "$SHUKAH_BASE_URL/maeda/api/verify-otp" --data-urlencode "contact_phone=$PHONE" --data-urlencode "code=$CODE" --data-urlencode "platform=iOS"

Captured Shukah response. 200 on dev, 2026-10-02 (correct code 2022, platform=iOS, no token). The access_token value is redacted, so its kind was not recorded here. The data profile is the same 13 fields with equal values as on Locate.

{
"access_token": "<redacted>",
"data": {
"client_id": 356259,
"odoo_id": "1342170",
"contact_phone": "+966500000000",
"status": "active",
"email_address": "user@example.com",
"first_name": "Test",
"last_name": "User",
"sex": 1,
"birth_date": "1990-01-01",
"receive_offers": 0,
"push_enabled": 0,
"is_food_truck_user": 1,
"is_cod_disabled": 0
}
}

The two tokens issued earlier still worked after this call (Shukah GET /maeda/client/points 200): a new login does not end earlier sessions. What Shukah does with fcm_token and platform is still not visible in the answer.

Errors seen. Wrong code, 400 (captured on dev, 2026-10-02): {"name":"Bad Request","message":"Sorry, but your verification code is incorrect","code":0,"status":400,"type":"Symfony\\Component\\HttpKernel\\Exception\\HttpException"}. The July 2026 capture kept in the auth service's draft doc (api-auth-draft.md, 2026-07-07) had the same message with the Yii type yii\\web\\BadRequestHttpException.

FieldShukahLocateNote
contact_phone (query)Digits.Whitespace removed, matched with or without +.
code (query)4 digits.Compared as text, so a leading zero matters.
fcm_token (query)Sent when the app has one. What Shukah does with it: Not documented yet.Dropped.If Shukah used it, that is lost. The app also saves its FCM token separately, to order-v2 (client/fcm/save-token).
platform (query)Android, iOS or Huawei. What Shukah does with it: Not documented yet.Dropped.
language (query)Sent.Ignored.
access_token (response)An opaque Shukah token.A Locate JWT, never expires.Every later /api/v1/clients/* call needs the JWT.
data (response)The same 13-field profile as Locate, with equal values (captured on dev, 2026-10-02).The 13-field profile, as in verify-token.None: the shape and the values matched in the 2026-10-02 pair.
Wrong-code errorThe Laravel body above (Symfony type).{"status":400,"message":"Sorry, but your verification code is incorrect"} (captured on dev, 2026-10-01 and 2026-10-02).name, code and type are gone. One message covers wrong, expired, used and unknown phone.

GET /maeda/api/verify-token​

Locate replacement: GET /api/v1/clients/verify-token

Full dev URL: https://dev-api.shukah.com/maeda/api/verify-token

Request. Authorization: Bearer <stored token>, language. The app also sets Accept, Content-Type and x-from-application: LOCATE_CLIENT_V2 explicitly on this call. It runs at every app start and again when Home opens.

curl -s "$SHUKAH_BASE_URL/maeda/api/verify-token" -H "Authorization: Bearer $SHUKAH_TOKEN"

Captured Shukah response. 200, 731 ms.

{
"client_id": 356259,
"odoo_id": "1342170",
"contact_phone": "+966500000000",
"status": "active",
"email_address": "user@example.com",
"first_name": "Test",
"last_name": "User",
"sex": 1,
"birth_date": "1990-01-01",
"receive_offers": 0,
"push_enabled": 0,
"is_food_truck_user": 1,
"is_cod_disabled": 0
}

Captured on dev, 2026-10-01 (client 356259); personal fields replaced. On 2026-10-02 the Shukah body and the Locate body (with a Locate JWT) were equal again, key for key. The app's profile type ignores odoo_id, is_food_truck_user and is_cod_disabled.

Errors seen. No token, 401 (captured on dev, 2026-10-01): the Laravel body shown under Error bodies. On any 401 the app deletes its stored token.

FieldShukahLocateNote
client_id, odoo_id, contact_phone, status, email_address, first_name, last_name, sex, birth_date, receive_offers, push_enabled, is_food_truck_user, is_cod_disabled (response)The 13 keys above.The same 13 keys, types and values for client 356259 (captured on dev, 2026-10-01).The body is a drop-in.
Authorization (header)A Shukah opaque token.A Locate JWT only.A Shukah token gets 401. See Tokens.
language, x-from-application and other headersSent.Accepted and ignored.
401 bodyLaravel body above.{"status":401,"message":"Unauthorization"} (captured on dev, 2026-10-01).Anything that matches on the text, name or type must change.
App-version response headersWhether Shukah sends them: Not documented yet.Not sent. The Locate URL does not contain api/verify-token.See Request headers.

GET /maeda/client​

Locate replacement: GET /api/v1/clients

Full dev URL: https://dev-api.shukah.com/maeda/client?contact_phone=<digits>&key=maeda&language=en-GB

Used to find a gift recipient by phone. Request. Bearer token. Query contact_phone (digits, the app strips +), key=maeda, language.

curl -s -G "$SHUKAH_BASE_URL/maeda/client" -H "Authorization: Bearer $SHUKAH_TOKEN" --data-urlencode "contact_phone=$PHONE" --data-urlencode "key=maeda"

Captured Shukah response. 200, 867 ms. An array with one object.

[
{
"client_id": 356259,
"contact_phone": "+966500000000",
"first_name": "Test",
"last_name": "User"
}
]

Captured on dev, 2026-10-01 (client 356259); personal fields replaced. The capture replaced last_name, so whether Shukah masks it is not visible: Not documented yet.

Errors seen. No token, 401, the Laravel body (captured on dev, 2026-10-01). The app takes only the first row and treats an empty array as "User not found".

FieldShukahLocateNote
client_id (response)Integer.Integer.
contact_phone (response)The phone.The phone as stored, not masked.
first_name (response)The first name.Trimmed; blank becomes null. Not masked.
last_name (response)Not documented yet (masking cannot be seen in the capture).Masked: first and last character kept, the middle becomes * (User becomes U**r); one or two characters stay as they are.The app copies it from the first row. Check the gift flow.
Unknown phoneNot documented yet.200 with [], also for a client that is not active.The app already treats [] as "User not found".
Authorization (header)A Shukah token.A Locate JWT only.See Tokens.
key, language (query)Sent.Ignored.
401 bodyLaravel body.{"status":401,"message":"Unauthorization"}.

GET /maeda/address-book​

Locate replacement: GET /api/v1/clients/address-book

Full dev URL: https://dev-api.shukah.com/maeda/address-book (no api/ segment).

Request. Bearer token. No params beyond language.

curl -s "$SHUKAH_BASE_URL/maeda/address-book" -H "Authorization: Bearer $SHUKAH_TOKEN"

Captured Shukah response. 200, 770 ms. A flat array of 13 addresses for client 356259; the first:

{
"id": 162115,
"location_name": "Saihat",
"is_default": 0,
"formatted_address": "King Fahd Rd, Al Olaya, Riyadh 12211",
"area_name": "A12345",
"street": "King Fahd Rd",
"city": "Saihat",
"area": "Eastern Province",
"state": "Al-Qatif",
"zipcode": "32461",
"country": "Saudi Arabia",
"country_code": "SA",
"lat": 24.7136,
"lng": 46.6753,
"image_urls": ["https://example.com/address-photo.jpg"],
"position": 0
}

Captured on dev, 2026-10-01 (client 356259); personal fields and coordinates replaced. None of the first 10 addresses is default. On 2026-10-02 the list had 15 addresses, all with is_default 0, ordered by position 0 to 14 while the ids are not in order (162115, 161996, 162101, …).

Errors seen. None for this route. The path /maeda/api/address-book answers 404 {"name":"Not Found","message":"The route maeda/api/address-book could not be found.",…} (captured 2026-10-01): the app does not call it.

FieldShukahLocateNote
idInteger (162115).24-character string.The app types it as a number.
is_default0 or 1.Boolean.A truthiness test works for both. A strict compare with 1 would not.
location_name, formatted_address, area_name, street, city, area, state, zipcode, country, country_codeStrings.Strings, null when missing (list shape).
lat, lngJSON numbers.JSON numbers on the list.
image_urlsAn array or null. Four of the 15 addresses have a photo URL (2026-10-02).Always an array. [] on the Locate copies of those four addresses (2026-10-02).The photos are lost in the copy to Locate. Cause: Not documented yet.
positionInteger.Not returned.The app does not read it.
source, created_at, updated_atNot returned.source (mt_client_inline, mt_address_book or locate) and the two ISO UTC dates.The app does not read them.
OrderBy position, 0 to 14 (captured on dev, 2026-10-02).Newest created_at first.The list can show in a different order.
Count for client 35625915 addresses (2026-10-02; 13 on 2026-10-01).30 addresses, same client, same day (28 on 2026-10-01).Deletes made on Shukah never reach Locate, and one address appears three times.
Default addressesis_default 0 on all 15 (2026-10-02).true on 28 of 30 (2026-10-02).The app takes the first default it finds.
AuthorizationA Shukah token.A Locate JWT only.

GET /maeda/address-book/:id​

Locate replacement: GET /api/v1/clients/address-book/:id

Full dev URL: https://dev-api.shukah.com/maeda/address-book/<id>

Request. Bearer token. Path id, an integer from the list.

curl -s "$SHUKAH_BASE_URL/maeda/address-book/$ADDRESS_ID" -H "Authorization: Bearer $SHUKAH_TOKEN"

Captured Shukah response. 200, 286 ms: the same 16 keys as the list item above, flat.

{
"id": 162115,
"location_name": "Saihat",
"is_default": 0,
"formatted_address": "King Fahd Rd, Al Olaya, Riyadh 12211",
"area_name": "A12345",
"street": "King Fahd Rd",
"city": "Saihat",
"area": "Eastern Province",
"state": "Al-Qatif",
"zipcode": "32461",
"country": "Saudi Arabia",
"country_code": "SA",
"lat": 24.7136,
"lng": 46.6753,
"image_urls": ["https://example.com/address-photo.jpg"],
"position": 0
}

Captured on dev, 2026-10-01 (client 356259); address text and coordinates replaced.

Errors seen. After a delete, 404 (captured on dev, 2026-10-01): {"name":"Not Found","message":"Object not found: 162119","code":0,"status":404,"type":"Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException"}. Another client's address (id 1), 403 (captured on dev, 2026-10-02): {"name":"Forbidden","message":"You can only view address that belong to you.","code":0,"status":403,"type":"Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException"}.

FieldShukahLocateNote
idInteger.24-character string. A numeric Shukah id still works in the path for addresses that came from Shukah.The app compares its stored last-selected id as a string, so a saved Shukah integer no longer matches.
is_default0 or 1.0 or 1.The same type, but not always the same value: address 162115 is 0 on Shukah and 1 on Locate (2026-10-02).
lat, lngJSON numbers.Strings ("24.7136").The app converts with Number().
Text fieldsStrings.Strings, "" when missing, never null.
image_urlsAn array or null.Always an array.Address 162115 has one photo URL on Shukah and [] on Locate (2026-10-02).
positionInteger.Not returned.
Someone else's address403 with the Laravel body above.404 Address not found, as for an unknown id (code reading; a foreign id was not tried live on Locate).The status and the text the app shows change.
404 bodyThe Laravel body above.{"status":404,"message":"Address not found"} (captured on dev, 2026-10-01). On dev it also carries a stack field with a server stack trace.The text the app shows changes.

POST /maeda/address-book​

Locate replacement: POST /api/v1/clients/address-book

Full dev URL: https://dev-api.shukah.com/maeda/address-book

Request (from the app). JSON with lat and lng as strings, is_default (0 or 1), area_name, location_name and image_urls (an array or null). Before the call the app deletes any existing address with the same location_name, so one save is a delete and then this create.

# Creates an address on dev. Delete it afterwards.
curl -s -X POST "$SHUKAH_BASE_URL/maeda/address-book" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "Content-Type: application/json" -d '{"lat":"24.7136","lng":"46.6753","is_default":0,"area_name":"Olaya","location_name":"Docs test","image_urls":[]}'

Captured Shukah response. 200, 678 ms. The new address, flat. Shukah filled in street, city, area, state, zipcode, country and formatted address from the coordinates.

{
"id": 162119,
"location_name": "Docs capture (Shukah)",
"is_default": 0,
"formatted_address": "King Fahd Rd, Al Olaya, Riyadh 12211",
"area_name": "Olaya",
"street": "King Fahd Rd",
"city": "Riyadh",
"area": "Riyadh Province",
"state": "Riyadh Principality",
"zipcode": "12251",
"country": "Saudi Arabia",
"country_code": "SA",
"lat": 24.7136,
"lng": 46.6753,
"image_urls": [],
"position": 13
}

Captured on dev, 2026-10-01 (client 356259, request body as in the curl but with the name Docs capture (Shukah)); deleted afterwards.

Errors seen. None captured.

FieldShukahLocateNote
lat, lng (body)Strings from the app.Numbers or numeric strings; anything else is stored as null.
is_default (body)0 or 1.1, "1" or true mean default; anything else does not.On Locate a default also clears the flag on other addresses.
area_name, location_name (body)Sent.Stored trimmed.
image_urls (body)Array or null.Array; strings trimmed, blanks dropped.
street, city, area, state, zipcode, country, country_code, formatted_addressFilled in by Shukah from the coordinates (captured again on 2026-10-02). How: Not documented yet.Stored only if sent, else null: all eight came back null for the app's body (captured on dev, 2026-10-02).An address created with the app's current body has no street or city on Locate.
Response shapeThe address itself, flat, with an integer id and position.{"status":200,"address":{…}} with a 24-character id, source: "locate" and the ISO dates.The app reads id, lat, lng at the top level, so with the wrapper they are missing. Read address.
Where it is storedShukah.Locate, then a best-effort copy to Shukah.On dev the copy did not reach Shukah on 2026-10-01 or on 2026-10-02: the copy is posted to /maeda/api/address-book, which dev Shukah answers with 404.

PUT /maeda/address-book/:id​

Locate replacement: PUT /api/v1/clients/address-book/:id

Full dev URL: https://dev-api.shukah.com/maeda/address-book/<id>

Request (from the app). Path id. JSON with any of street, city, location_name, state, zipcode, country_code, country, area, formatted_address, is_default (0 or 1) and image_urls. The app's Edit address screen sends location_name, area, is_default and image_urls. There is no lat, lng or area_name on update.

# Edits an address on dev. Use one you created.
curl -s -X PUT "$SHUKAH_BASE_URL/maeda/address-book/$ADDRESS_ID" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "Content-Type: application/json" -d '{"location_name":"Docs test (edited)"}'

Captured Shukah response. 200, 245 ms. The full address, flat, with the changed field.

{
"id": 162119,
"location_name": "Docs capture (Shukah, edited)",
"is_default": 0,
"formatted_address": "King Fahd Rd, Al Olaya, Riyadh 12211",
"area_name": "Olaya",
"street": "King Fahd Rd",
"city": "Riyadh",
"area": "Riyadh Province",
"state": "Riyadh Principality",
"zipcode": "12251",
"country": "Saudi Arabia",
"country_code": "SA",
"lat": 24.7136,
"lng": 46.6753,
"image_urls": [],
"position": 13
}

Captured on dev, 2026-10-01 (client 356259, body {"location_name":"Docs capture (Shukah, edited)"}); deleted afterwards.

Errors seen. None captured. A missing id answers the Laravel 404 shown under get.

FieldShukahLocateNote
id (path)Integer.A 24-character id, or digits for a Shukah id.
Body keysThe keys above; partial update.The same keys, partial. A key sent as null, "" or spaces clears the field (image_urls becomes []).
is_default0 or 1.Only when the key is in the body. Setting it moves the default. Shukah's default rules: Not documented yet.
ResponseFlat address, integer id, numeric lat and lng, position.Flat address, 24-character id, string lat and lng, no position.The keys the app reads are all there.
Copy to ShukahNot applicable.A best-effort copy. With no Shukah id it creates a new address there, which can duplicate it.On dev the edit of a Locate-made address did not reach Shukah on 2026-10-02. An edit made on Shukah did not reach Locate either.

DELETE /maeda/address-book/:id​

Locate replacement: DELETE /api/v1/clients/address-book/:id

Full dev URL: https://dev-api.shukah.com/maeda/address-book/<id>

Request. Path id, no body. The app turns the id into a number with a unary plus first.

# Deletes an address on dev, permanently. Use one you created.
curl -s -X DELETE "$SHUKAH_BASE_URL/maeda/address-book/$ADDRESS_ID" -H "Authorization: Bearer $SHUKAH_TOKEN"

Captured Shukah response. 204 with an empty body, 286 ms (captured on dev, 2026-10-01). The app does not read the body.

Errors seen. A second read of the deleted id answers the Laravel 404 Object not found: 162119 (captured on dev, 2026-10-01).

FieldShukahLocateNote
id (path)Integer.A 24-character id, or digits for a Shukah id.The app's unary plus turns a 24-hex id into NaN, so the call becomes .../address-book/NaN and gets 404.
Success204, empty body.200 {"status":200}.None: the app does not read the body.
404 bodyThe Laravel body.{"status":404,"message":"Address not found"}. A second delete also gets it.
Copy to ShukahNot applicable.The delete is never copied to Shukah.An address edited through Locate and then deleted can stay on Shukah.

PUT /maeda/client/:client_id​

Locate replacement: PUT /api/v1/clients/:client_id

Full dev URL: https://dev-api.shukah.com/maeda/client/<client_id>

This one Shukah route serves two registry entries. The profile edit and the push-notification switch both call PUT /maeda/client/<client_id>. They are told apart only by the body: profile edits carry profile keys, the push switch carries {client_id, push_enabled}. Locate splits them: this entry maps to PUT /api/v1/clients/:client_id, and update setting maps to PUT /api/v1/clients/setting. The app has no separate Shukah setting call.

Request (from the app). Path client_id (the number, repeated in the body). JSON client_id plus any of contact_phone, email_address, first_name, last_name, sex (0, 1, 2 or 9), birth_date (YYYY-MM-DD, or null) and receive_offers (0 or 1).

# Writes the profile on dev. Send the client's current value back.
curl -s -X PUT "$SHUKAH_BASE_URL/maeda/client/$CLIENT_ID" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "Content-Type: application/json" -d "{\"client_id\":$CLIENT_ID,\"receive_offers\":$CURRENT_RECEIVE_OFFERS}"

Captured Shukah response. 200, 239 ms (captured on dev, 2026-10-02, with the body {"client_id":356259,"receive_offers":0}, the current value written back): the 13-field profile, with the same keys, types and values as the verify-token sample. Locate answered the same body. The app parses it as UserServer.

Errors seen. A bad birth_date ("not-a-date"), 422 (captured on dev, 2026-10-02): [{"field":"birth_date","message":"The birth date field must match the format Y-m-d."}]. It is an array of field errors, not the Laravel error object. Locate answered 400 {"status":400,"message":"birth_date is invalid","stack":"…"} to the same body. Shukah's other validation rules: Not documented yet.

FieldShukahLocateNote
client_id (path and body)The numeric id, in both. Whether Shukah checks it against the token: Not documented yet.Must equal the token's client in the path, and in the body if sent, else 400 client_id does not match the authenticated client.
contact_phoneWhether a changed phone is accepted: Not documented yet.Must equal the stored phone exactly, with the leading +, else 400 contact_phone cannot be changed. Never written.Do not reformat the phone.
first_name, last_nameStrings.null or at most 100 characters after trimming, else 400.
email_addressString.Any string or null, no format check.
birth_dateYYYY-MM-DD or null.Anything new Date() can read, stored as the UTC date. null clears it. "" or a bad value is 400 birth_date is invalid.
sex0, 1, 2 or 9.1 male, 2 female. Anything else is stored as unknown and read back as 0.A Shukah 9 reads as unknown after the switch.
receive_offers0 or 1.1, "1" or true mean on, anything else off.
push_enabledAccepted on this route: the push switch uses it.Ignored. The call answers 200 with the unchanged profile.Move the switch to update setting, or it silently does nothing.
ResponseThe 13-field profile (captured on dev, 2026-10-02).The same 13-field profile (captured on dev, 2026-10-01 and 2026-10-02).
Errors422 with an array of {field, message} (captured for birth_date, 2026-10-02).400 {"status":400,"message":"…"}, first failing check only. On dev the body also has a stack field.The app shows message, so it loses the field name, and the status changes from 422 to 400.
AuthorizationA Shukah token.A Locate JWT only.
Copy to ShukahNot applicable.Not copied.Anything still reading the profile from Shukah keeps the old values.

PUT /maeda/client/:client_id​

Locate replacement: PUT /api/v1/clients/setting

Full dev URL: https://dev-api.shukah.com/maeda/client/<client_id>

The same Shukah route as update profile. It is the push switch when the body is only {client_id, push_enabled}.

Request (from the app). JSON {"client_id": <id>, "push_enabled": 0 or 1}.

# Writes the push setting on dev. Send the current value back.
curl -s -X PUT "$SHUKAH_BASE_URL/maeda/client/$CLIENT_ID" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "Content-Type: application/json" -d "{\"client_id\":$CLIENT_ID,\"push_enabled\":$CURRENT_PUSH_ENABLED}"

Captured Shukah response. 200, 252 ms (captured on dev, 2026-10-02, with the body {"client_id":356259,"push_enabled":0}, the current value written back): the 13-field profile with push_enabled 0, as in the verify-token sample. Locate's setting route answered the same body. The app reads push_enabled from it. Because the value written was the current one, the capture does not prove that Shukah applies push_enabled on this route.

Errors seen. None captured: Not documented yet.

FieldShukahLocateNote
RoutePUT /maeda/client/<client_id>, the profile route.PUT /api/v1/clients/setting, no id in the path.Point the push switch at the new route.
client_id (body)Sent. Whether Shukah checks it: Not documented yet.Required. Must equal the token's client, else 400 client_id does not match the authenticated client.The app already sends it.
push_enabled (body)0 or 1.Required. 1, "1" or true switch it on, everything else off. A missing key is 400 push_enabled is required.
Other keysProfile keys are accepted on the same route.Not accepted: only client_id and push_enabled.
ResponseThe 13-field profile (captured on dev, 2026-10-02).The 13-field profile with the new push_enabled (captured on dev, 2026-10-01 and 2026-10-02).
AuthorizationA Shukah token.A Locate JWT only.
Copy to ShukahNot applicable.Not copied. Shukah keeps the old value.

POST /maeda/client/add-device-id​

Locate replacement: POST /api/v1/clients/add-device-id

Full dev URL: https://dev-api.shukah.com/maeda/client/add-device-id

Request (from the app). JSON {"device_id": "<device unique id>"}. The app sends it once per install, only when signed in, and records that it was sent after a 200.

curl -s -X POST "$SHUKAH_BASE_URL/maeda/client/add-device-id" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "Content-Type: application/json" -d '{"device_id":"00000000-0000-4000-8000-000000000000"}'

Captured Shukah response. 200, 236 ms (captured on dev, 2026-10-02, with the dummy device id of the curl above). The app ignores the answer.

{
"id": 1074,
"client_id": 356259,
"device_id": "00000000-0000-4000-8000-000000000000"
}

Shukah stored a new device row (1074) for the caller. No API removes it, so the dummy row is still on dev.

Errors seen. None captured.

FieldShukahLocateNote
device_id (body)The device id. Shukah stores a new device row for the caller on each call (2026-10-02).Echoed back. Not validated, not stored.
id (response)The new device row's id (1074).Always 920, for every caller.A fixed stand-in.
client_id (response)The caller's id (356259).Always 356315, not the caller's id.A fixed stand-in.
device_id (response)Echoed.Echoed; a fixed sample id when the body has none.
AuthorizationThe app sends its token. Whether Shukah requires it: Not documented yet.Not needed, not checked.
EffectA device row stored for the caller (2026-10-02).None: nothing is stored.The app marks the device as sent and never retries, although nothing was stored.

GET /maeda/client/status​

No Locate replacement yet — whatever calls this route keeps calling Shukah.

Full dev URL: https://dev-api.shukah.com/maeda/client/status

Request shape. Not documented yet. The app snapshot of 2026-02-26 declares this endpoint, but no code in it calls the endpoint, so the request it would send is not known.

curl -s "$SHUKAH_BASE_URL/maeda/client/status" -H "Authorization: Bearer $SHUKAH_TOKEN"

Captured Shukah response. 404, 320 ms (captured on dev, 2026-10-02): dev Shukah has no such route.

{
"name": "Not Found",
"message": "The route maeda/client/status could not be found.",
"code": 0,
"status": 404,
"type": "Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException"
}

Errors seen. The 404 above.

What is known:

  • The production traffic sheet of 2026-10-02 shows 768 calls to this route in 3 days, so something calls it in production. Which client sends them: Not documented yet. App 2.31.5 is newer than the snapshot, so ask the mobile team which screen calls it.
  • Dev Shukah answers 404: the route is missing from the Laravel port on dev. Whether production still serves it: Not documented yet. If app 2.31.5 calls it, that call fails on dev today.
  • No Locate route was found for it. No code read for the audit (the app snapshot, the auth service, order-v2 and three other Locate backends) calls it either; order-v2's own status route is a disaster-recovery check and is unrelated. What the route does: Not documented yet.

GET /maeda/client/delete​

No Locate replacement yet — the app keeps calling Shukah.

Full dev URL: https://dev-api.shukah.com/maeda/client/delete

Warning: this route deletes the account, and it is a GET. It was never called while this page was written, with any token. There is no runnable command here on purpose. Do not run it against a real account, and do not let a link preview, a crawler or a health check issue it.

Request shape. GET, no query beyond language, no body. Authorization: Bearer <stored token> is the only input. The reason the user picks on the delete screen is not sent.

Captured Shukah response. None: never called. The app expects no body. On success the app signs the user out locally. A 401 makes the app delete its stored token.

Errors seen. None.

What is known:

  • No route in the auth service, the point service or order-v2 deletes an account. The app keeps calling this Shukah route, which needs a token Shukah accepts (see Tokens).
  • What Shukah does to the record (soft delete, status change) and how that reaches Locate: Not documented yet. No Locate code sets a client as deleted.

Points APIs​

GET /maeda/client/points​

Locate replacement: GET /api/v2/client/points

Full dev URL: https://dev-api.shukah.com/maeda/client/points

Request. Bearer token, language. No other params. A second app caller (the food-court package) adds x-auth-from: SHUKAH.

curl -s "$SHUKAH_BASE_URL/maeda/client/points" -H "Authorization: Bearer $SHUKAH_TOKEN"

Captured Shukah response. 200, 251 ms.

{
"id": "356259",
"user_id": "356259",
"points": "514853.64",
"pointsExpirationDate": "2026-11-30"
}

Captured on dev, 2026-10-02 (client 356259). On 2026-10-01 the same call returned "128712.41", so the Shukah dev data changed in between.

Errors seen. No token, 401, the Laravel body (captured on dev, 2026-10-01).

FieldShukahLocateNote
idThe client id as text.The client id as text.
user_idThe same as id.The same as id.
pointsString with two decimals: "514853.64" (2026-10-02).String with up to four decimals: "514405.6788" (same client, same day, with a Shukah token).The app uses Number(points) || 0. Locate is 447.96 points lower, about 0.09%. Locate's newest ledger row is two days older than Shukah's, which fits the gap (inference).
pointsExpirationDate"2026-11-30"."2026-11-28".Two days earlier; null unless the expiry rule applies.
AuthorizationA Shukah token.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 both worked.The app must keep a Shukah token for this route. See Tokens.
Error bodyLaravel body.The point-service envelope.
App-version headersWhether Shukah sends them on this URL: Not documented yet.Not sent. The Locate URL also contains client/points, so the app would look.See Request headers.

GET /maeda/point-rate​

Locate replacement: GET /api/v2/client/system_configs/point_rates

Full dev URL: https://dev-api.shukah.com/maeda/point-rate

Public on Shukah: 200 with no token. The app calls the Locate route instead only in Merchant mode (enableMerchantService: true in the client settings).

Request. No params beyond language.

curl -s "$SHUKAH_BASE_URL/maeda/point-rate"

Captured Shukah response. 200, 235 ms.

[
{"amount_from": 0, "amount_to": 139, "point_earn": 2},
{"amount_from": 140, "amount_to": 153, "point_earn": 12},
{"amount_from": 154, "amount_to": 250, "point_earn": 16},
{"amount_from": 251, "amount_to": 450, "point_earn": 20},
{"amount_from": 451, "amount_to": null, "point_earn": 30}
]

Captured on dev, 2026-10-01.

Errors seen. None.

FieldShukahLocateNote
amount_fromInteger, starts at 0.Starts at 70.The app adds a row below the first, so a total under 70 shows 0 points on Locate.
amount_toInteger, null on the last row. Ranges do not overlap (139, then 140).Integer, null on the last row. Ranges share an end value (140 ends one row and starts the next).A total of exactly 140 or 251 can match two rows.
point_earn2, 12, 16, 20, 30.2, 4, 10, 16.Different tiers on dev.
RowsFive.Four: 70-140, 140-251, 251-451, 451 and up (captured on dev, 2026-10-01).Which table is right: Not documented yet.
Empty answerNot documented yet.[] when the option is not set.The app reads the first row without checking.
AuthorizationNone needed.None needed.
Credit ruleNot applicable.order-v2 still computes the points an order earns from Shukah's table.The card can differ from the points actually credited.

GET /maeda/giftcard-code/use​

Locate replacement: POST /api/v2/client/gift-card-code

Full dev URL: https://dev-api.shukah.com/maeda/giftcard-code/use?code=<code>

Request. Bearer token. Query code. The app calls the Locate route instead only in Merchant mode (enableMerchantService: true).

Warning: a GET that credits points when the code is valid. It was never called while this page was written.

# Credits points if the code is valid. Use a code you may spend.
curl -s -G "$SHUKAH_BASE_URL/maeda/giftcard-code/use" -H "Authorization: Bearer $SHUKAH_TOKEN" --data-urlencode "code=$GIFT_CODE"

Captured Shukah response. None: it credits points. The app expects six keys: total_points, date_created, description, message, points_type, trans_type. Shukah's real values: Not documented yet.

Errors seen. None captured.

FieldShukahLocateNote
Method and codeGET with ?code=.POST with {"code":"<code>"} in the body.Change the method, the URL and where the code goes.
total_points, date_created, description, message, points_type, trans_type (response)The six keys the app parses. Real values: Not documented yet.The same six keys. date_created is yyyy-mm-dd hh:mm:ss in Arab time.The app parses the same keys in both modes.
Code inventoryShukah's own codes.The codes in order-v2's merchant database. Whether Shukah-issued codes are there: Not documented yet.A code that works on Shukah may be refused.
AuthorizationA Shukah token.The same token check as the point routes (via /api/v1/users); not captured with a token.
Where the credit landsNot applicable.Shukah or the point service, by a server flag (off by default: Shukah first). Dev value: Not documented yet.With the flag off, the Locate balance moves only after the sync.
Error bodyNot documented yet.Nest: {"message":"Unauthorized","statusCode":401} for a missing token.

GET /maeda/client/point-history​

Locate replacement: GET /api/v2/client/point-history

Full dev URL: https://dev-api.shukah.com/maeda/client/point-history

Request. Bearer token. No page or limit.

curl -s "$SHUKAH_BASE_URL/maeda/client/point-history" -H "Authorization: Bearer $SHUKAH_TOKEN"

Captured Shukah response. 200, 2,254 ms: one JSON array of 8,134 rows (8,136 on 2026-10-02). The first earn row:

{
"shukah_id": 1351762,
"total_points": 0.5,
"date_created": "2026-10-01 10:40:44",
"description": "Points gained by buy item Order ID L07678739",
"message": "",
"points_type": "earn",
"trans_type": "adjustment"
}

Captured on dev, 2026-10-01 (client 356259), first of 8,134 rows (the capture keeps the first 10, all earn rows). Expense rows were not in the stored rows; their 14 keys (id, client_id, merchant_id, order_id, total_points as a string such as "684.00", status, date_created, ip_address, total_points_amt, trans_type, points_type, remarks, external_id, description) come from the point service's parity report, locate-point-service: docs/reports/2026-07-17-client-api-parity.md (measured 2026-07-17).

Errors seen. No token, 401, the Laravel body (captured on dev, 2026-10-01).

FieldShukahLocateNote
PagingNone: the whole history.page and limit, default 20, maximum 100.The app sends neither, so it would show the newest 20 rows.
shukah_id (earn)Integer.Integer, or null for rows Locate wrote.
total_points (earn)A number.A number.No scale gap on 2026-10-02: 2, 2, 2, 2, 10 on Shukah's newest rows (0.5, 0.5, 2.5 on 2026-10-01).
date_createdYYYY-MM-DD HH:mm:ss, Riyadh.The same.Newest row 2026-10-01 17:00:01 on Shukah, 2026-09-29 12:45:07 on Locate (2026-10-02): Locate lags.
descriptionText, for example Points gained by buy item Order ID L07678739."" on 8 of the 9 earn rows among the first 10, manual credit on one (2026-10-02).The app would show a blank line.
message""."".
points_typeearn or expenses.The same.
trans_typeadjustment on the captured earn rows.adjustment on earn, order on the captured expense row.
Expense idAn integer.A 24-character string.The app's history type has no id.
Expense total_pointsA string with two decimals.A string with exactly two decimals ("360.00").
Expense client_id, merchant_id, order_id, status, ip_address, total_points_amt, remarks, external_idPresent on Shukah expense rows (July 2026 parity report); values not captured.Present: status always "active", ip_address always "", external_id always null.
Expiration rowsA negative earn row.An expense row with a positive amount for expirations since 2026-09-16; older ones are negative earn rows.
Error bodyLaravel body.The point-service envelope.

GET /maeda/point-offer​

Locate replacement: GET /api/v2/client/point-offers

Full dev URL: https://dev-api.shukah.com/maeda/point-offer

Public on Shukah: 200 with no token.

Request. No params for the list. The app also calls it with ?id=<id> and then picks the offer out of the returned list itself.

curl -s "$SHUKAH_BASE_URL/maeda/point-offer"

Captured Shukah response. 200, 235 ms: five offers.

[
{"id": 1, "point_amount": 100, "purchased_point_amount": 110, "amount": "40.00"},
{"id": 2, "point_amount": 200, "purchased_point_amount": 240, "amount": "60.00"},
{"id": 3, "point_amount": 300, "purchased_point_amount": 360, "amount": "90.00"},
{"id": 4, "point_amount": 400, "purchased_point_amount": 480, "amount": "120.00"},
{"id": 5, "point_amount": 500, "purchased_point_amount": 600, "amount": "150.00"}
]

Captured on dev, 2026-10-01.

Errors seen. None.

FieldShukahLocateNote
idInteger, 1 to 5.24-character string, for example 6a46104f3dd7b3dd6a261d90.Never mix the two kinds: Locate refuses a Shukah integer.
point_amountInteger.Equal values on 2026-10-01.
purchased_point_amountInteger.Equal values, but can be null when the offer has no points price.The app types it as a number.
amountString with two decimals.Equal values on 2026-10-01.
?id= (query)Returns the whole list.Accepted and ignored: the whole list again.The app's client-side pick works on the new ids.
AuthorizationNone needed.Required: 401 with no token (captured on dev, 2026-10-01).A screen that shows offers before login would fail.
OrderNot documented yet.By admin-set sequence, then Shukah id.
Error bodyNot documented yet (public).The point-service envelope.

GET /maeda/point-occasion​

Locate replacement: GET /api/v2/client/point-occasions

Full dev URL: https://dev-api.shukah.com/maeda/point-occasion

Public on Shukah: 200 with no token.

Request. No params for the list; the app also calls it with ?id=<id> and filters the list itself.

curl -s "$SHUKAH_BASE_URL/maeda/point-occasion"

Captured Shukah response. 200, 249 ms: seven occasions.

[
{"id": 1, "occasion": "Birthday", "occasion_en": "Birthday", "is_custom": 0},
{"id": 2, "occasion": "Born", "occasion_en": "Born", "is_custom": 0},
{"id": 3, "occasion": "Gift", "occasion_en": "Gift", "is_custom": 0},
{"id": 4, "occasion": "Wedding", "occasion_en": "Wedding", "is_custom": 0},
{"id": 5, "occasion": "Graduation", "occasion_en": "Graduation", "is_custom": 0},
{"id": 6, "occasion": "Thank", "occasion_en": "Thank", "is_custom": 1},
{"id": 7, "occasion": "Other", "occasion_en": "Other", "is_custom": 1}
]

Captured on dev, 2026-10-01.

Errors seen. None.

FieldShukahLocateNote
idInteger, 1 to 7.24-character string.Never mix the two kinds.
occasionThe English text.English, or Arabic when language is exactly ar.The app sends ar-SA, so it never gets the Arabic label.
occasion_enThe English text.Equal values on 2026-10-01.The app's type reads occasionEn, so its English name is undefined today on both sides.
is_custom0 or 1.Equal values on 2026-10-01.
?id= (query)Returns the whole list.Ignored: the whole list again.
AuthorizationNone needed.Required: 401 with no token.
Error bodyNot documented yet (public).The point-service envelope.

POST /maeda/point-order​

Locate replacement: POST /api/v2/client/point-orders

Full dev URL: https://dev-api.shukah.com/maeda/point-order

Request (from the app). JSON point_offer_id (string), to_client_id (string), payment_type (point, or for an online payment the payment value from the common settings: payfort, hyperpay or checkout), and optionally point_occasion_id and occasion (free text). There is no idempotency key: a retried POST makes a second order.

Warning: creates an order. payment_type point moves points at once (the sender pays a markup: 110 for 100). Only try checkout, which stays unpaid. Each kind was called once on dev on 2026-10-02, with the user's approval (orders 1256 and 1257).

# Creates an unpaid order on dev.
curl -s -X POST "$SHUKAH_BASE_URL/maeda/point-order" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "Content-Type: application/json" -d "{\"point_offer_id\":\"$OFFER_ID\",\"to_client_id\":\"$TO_CLIENT_ID\",\"payment_type\":\"checkout\"}"

Captured Shukah response. 201, 320 ms: a checkout order to the client's own id.

{
"point_order_id": 1256,
"payment_point_order_id": "p-1256",
"client_id": 356259,
"to_client_id": 356259,
"point_amount": 100,
"purchased_point_amount": 110,
"amount": "40.00",
"status": "initial_order",
"payment_type": "checkout",
"point_occasion_id": null,
"occasion": "",
"description": ""
}

Captured on dev, 2026-10-02 (client 356259). The point order (1257, to another dev client) has the same twelve keys, with status successful. It cut the Shukah balance by 110 and left the Locate balance as it was.

Create answers with the same keys as the order rows of the sent-gifts list. The app expects them too, except that it types occasion_id: Shukah sends point_occasion_id (a number or null), so the app's occasionId is always undefined.

Errors seen. None captured.

FieldShukahLocateNote
point_offer_id (body)Integer as text.24-character string from the Locate list.A Shukah integer is 400.
to_client_id (body)String of the id.A positive integer; a numeric string is accepted.
payment_type (body)point, payfort, hyperpay or checkout.Only point or checkout.payfort and hyperpay are 400. If the settings say payfort or hyperpay, create fails.
point_occasion_id (body)Integer as text.24-character string, or none.
occasion (body)Free text.Trimmed, 1 to 255 characters.
HTTP status201.200 (captured on dev, 2026-10-02).None when the app accepts any 2xx.
point_order_id (response)Integer.Not returned. The order id is the UUID correlation_id.The pay and get routes take it in the path.
client_id, to_client_id, amount, occasion, point_occasion_id, description, payment_point_order_id (response)Read by the app, which expects occasion_id where Shukah's order rows have point_occasion_id.Not returned.The order object has to be rebuilt from the new keys.
correlation_id, order_no, payment_status, money_amount, order_type (response)Not returned.New: UUID, number from 100000, gateway state or null, two-decimal string, gift or transfer.
status, point_amount, purchased_point_amount, payment_type (response)The app's status words: initial_order, payment_failed, successful, paid.The same words (a Locate order reaches only the first three).
FlagNone.point needs POINT_ORDER_POINT_ENABLED; off answers 403. On for dev (probed 2026-10-01); production not confirmed.
Self-giftAllowed.Allowed for checkout, 400 for point.
Balance after a point orderOnly the Shukah balance drops (110 for order 1257).Only the Locate balance drops (110 for order 100012).The ledgers are not synced on a spend (dev, 2026-10-02, within seconds of the run; whether it arrives later was not checked). Read the balance and the history on the side that creates orders.
Error bodyNot documented yet.The point-service envelope.

POST /maeda/point-order/get-description​

Locate replacement: POST /api/v2/client/point-orders/description

Full dev URL: https://dev-api.shukah.com/maeda/point-order/get-description

Request (from the app). JSON point_offer_id (string), optionally point_occasion_id (string) and occasion (free text). The app calls it twice per order. It writes nothing.

curl -s -X POST "$SHUKAH_BASE_URL/maeda/point-order/get-description" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "Content-Type: application/json" -d '{"occasion":"","point_offer_id":"1","point_occasion_id":"1"}'

Captured Shukah response. 200, 857 ms. An empty description.

{
"description": ""
}

Captured on dev, 2026-10-01 (client 356259), with the body in the curl above, and again on 2026-10-02 with the same answer. Every body tried on 2026-10-01 returned the same: ids as text, ids as numbers, and with free text. The point service's doc says Shukah returned the full sentence on 2026-09-21 (locate-point-service: docs/integrations/2026-07-16-client-facing-point-apis.md, last changed 2026-09-22). Why it is empty now: Not documented yet.

Errors seen. None captured.

FieldShukahLocateNote
point_offer_id (body)Integer as text.24-character string.A Shukah integer is 400.
point_occasion_id (body)Integer as text.24-character string, or none.
occasion (body)Free text.Free text; when it is not empty it wins over the occasion id.
description (response)"" on dev today. The full sentence per the doc of 2026-09-21.You have received a gift Birthday of 100 points from Test User (*****0000) (captured on dev, 2026-10-01).With Shukah the message box would be empty today.
SentenceSame wording; a double space when there is no occasion.The same wording and the same double space.Do not trim it.
AuthorizationA Shukah token.A Shukah token (200 on dev, 2026-10-02). A Locate JWT was not tried here; the same check gives it 401 on the other point routes.See Tokens.
Error bodyNot documented yet.The point-service envelope.

POST /maeda/order/checkout-payment-standalone​

Locate replacement: POST /api/v2/client/point-orders/:correlation_id/pay/card

Full dev URL: https://dev-api.shukah.com/maeda/order/checkout-payment-standalone

The same Shukah route also pays restaurant orders. This section covers its use for point orders.

Warning: charges a saved card at the payment gateway. No runnable command here, and it was never called while this page was written.

Request shape. JSON {"id": "<saved card id>", "cvv": "<cvv>", "point_order_id": "<order id>"}. id is the app's card.tokenName. point_order_id is a string: the app 2.31.5 payment bodies captured on dev on 2026-10-02 send it as "1252" and "1253".

Captured Shukah response. None: it creates a payment. The app expects id, status, reference, 3ds (downgraded, enrolled) and _links (self, actions, redirect.href).

Errors seen. Dev Shukah answers the point-order payment routes with 422 (reported on 2026-10-02, from app 2.31.5 on dev): {"name":"Unprocessable Content","message":"Checkout payment is not configured properly.","code":0,"status":422,"type":"Symfony\\Component\\HttpKernel\\Exception\\HttpException"}. Dev Shukah cannot take a point payment today. Payment errors come from the payment service as its own 4xx.

FieldShukahLocateNote
id (body)The saved card id.The same.
cvv (body)The security code.A string or number, 1 to 8 characters. Forwarded, not stored.
point_order_id (body)A string such as "1252", in the body (app 2.31.5, 2026-10-02).Accepted and ignored. The order is the UUID correlation_id in the path.The app keeps only pointOrderId; it must keep the new id from create.
payment_type (body)Not sent on this route.Accepted and ignored.
ResponseThe app expects 3ds.downgraded and _links.redirect.The payment service's body unchanged; its sample has 3ds.is_redirect. A declined payment can have no _links.redirect.Check the 3-D Secure start against a real response.
FlagNone.POINT_ORDER_CARD_ENABLED; off answers 403 before any read. On for dev (probed 2026-10-01); production not confirmed.
Error bodyThe payment service's own.Passed through unchanged for payment errors; otherwise the point-service envelope.

POST /maeda/order/checkout-payment-flow​

Locate replacement: POST /api/v2/client/point-orders/:correlation_id/pay/session

Full dev URL: https://dev-api.shukah.com/maeda/order/checkout-payment-flow

Also used for restaurant orders. This section covers point orders.

Warning: opens a payment session at the payment gateway. No runnable command here. The docs never called it; app 2.31.5 called it on dev on 2026-10-02, and dev Shukah answered 422 (not configured).

Request shape. JSON {"point_order_id": "<order id>", "payment_type": "<wallet>", "rememberMe": true or false}. App 2.31.5 sent {"point_order_id":"1253","payment_type":"applePay","rememberMe":false} (captured on dev, 2026-10-02). point_order_id is a string. The app snapshot also allows stcpay and card as payment_type, but app 2.31.5 sends STC Pay to its own route, checkout-payment-stcpay.

Captured Shukah response. None. The app expects id, payment_session_secret, payment_session_token and _links.self.href.

Errors seen. The app 2.31.5 call above, 422 (captured on dev, 2026-10-02): {"name":"Unprocessable Content","message":"Checkout payment is not configured properly.","code":0,"status":422,"type":"Symfony\\Component\\HttpKernel\\Exception\\HttpException"}.

FieldShukahLocateNote
point_order_id (body)A string such as "1253" (app 2.31.5, 2026-10-02).Accepted and ignored. The order is the UUID correlation_id in the path.
payment_type (body)applePay in the 2.31.5 capture. The snapshot also allows stcpay and card.Accepted and ignored. The Checkout session offers the wallets.The wallet is no longer chosen on the server.
rememberMe (body)Boolean.true, false, "true" or "false"; default false. Anything else is 400.
ResponseThe app expects id, payment_session_secret, payment_session_token, _links.self.The payment service's session, unchanged, with the same keys.
FlagNone.POINT_ORDER_CARD_ENABLED, as above.
Error bodyNot documented yet.The point-service envelope.

GET /maeda/points-faq​

Locate replacement: GET /api/v2/client/points-faq

Full dev URL: https://dev-api.shukah.com/maeda/points-faq

Public on Shukah: 200 with no token.

Request. No params.

curl -s "$SHUKAH_BASE_URL/maeda/points-faq"

Captured Shukah response. 200, 233 ms: five items. The first (the Arabic text is shortened here):

{
"title_ar": "…",
"text_ar": "…",
"title_en": "How many points are worth in the Locate App?",
"text_en": "each point equal to 0.25 SR\r\nthat means, each 4 points equal to one SR"
}

Captured on dev, 2026-10-01. The five-item array was equal to Locate's, key order included.

Errors seen. None.

FieldShukahLocateNote
title_ar, text_ar, title_en, text_enStrings.Equal on 2026-10-01.A drop-in.
AuthorizationNone needed.Required: 401 with no token (captured on dev, 2026-10-01).A screen that shows the FAQ before login would fail.
Error bodyNot documented yet (public).The point-service envelope.
Content-Typeapplication/jsonapplication/json; charset=utf-8

GET /maeda/point-order​

No Locate replacement yet — the app keeps calling Shukah.

Full dev URL: https://dev-api.shukah.com/maeda/point-order?page=<n>&per-page=<n>

This is the "Sent gifts" tab: the only list of the gifts a user has sent.

Request. Bearer token. Query page and per-page.

curl -s -G "$SHUKAH_BASE_URL/maeda/point-order" -H "Authorization: Bearer $SHUKAH_TOKEN" --data-urlencode "page=1" --data-urlencode "per-page=20"

Captured Shukah response. 200, 251 ms, a bare array of 20 orders for page=1&per-page=20 (captured on dev, 2026-10-02). The first:

{
"point_order_id": 1255,
"payment_point_order_id": "p-1255",
"client_id": 356259,
"to_client_id": 356259,
"point_amount": 200,
"purchased_point_amount": 240,
"amount": "60.00",
"status": "initial_order",
"payment_type": "checkout",
"point_occasion_id": 3,
"occasion": "Gift",
"description": ""
}

The app expects an array of orders with point_order_id, client_id, to_client_id, point_amount, purchased_point_amount, amount, status, payment_type, occasion, occasion_id, description and payment_point_order_id. Shukah sends point_occasion_id, not occasion_id, so the app's occasion id is undefined. The app also reads the response headers x-pagination-total-count and x-pagination-page-count; the capture did not store response headers.

Errors seen. None.

What is known:

  • No Locate route lists a client's point orders. The point service and order-v2 have only create, get by correlation_id, description and the two pay routes. No plan names a list route.
  • The gift checkout opens this tab after paying, so an order made through Locate does not show there.
  • Locate has a get-by-correlation_id route, but it is new and is not a replacement for this list. The app also has GET /maeda/point-order/<id> in code, but no screen calls it.

POST /maeda/order/checkout-payment​

No Locate replacement yet — the app keeps calling Shukah.

Full dev URL: https://dev-api.shukah.com/maeda/order/checkout-payment

Warning: creates a payment at the payment gateway. No runnable command here.

Request shape. JSON {"payment_token": "<Checkout.com Frames token>", "point_order_id": <order id>}. The app uses it for point gifts when it gets a card token from Checkout.com Frames.

Captured Shukah response. None. The app expects the same keys as the standalone route (id, status, reference, 3ds, _links).

Errors seen. None.

What is known: Locate offers two ways to pay a point order, a saved card and a session. A Frames token has no counterpart. Which Locate call the app should use instead: Not documented yet.

POST /maeda/order/checkout-payment-stcpay​

No Locate replacement yet — the app keeps calling Shukah.

Full dev URL: https://dev-api.shukah.com/maeda/order/checkout-payment-stcpay

Warning: starts an STC Pay payment. No runnable command here.

Request shape. JSON {"point_order_id": "<order id>", "environment": "sandbox" or "production"}. App 2.31.5 sent {"point_order_id":"1252","environment":"sandbox"} (captured on dev, 2026-10-02): point_order_id is a string.

Captured Shukah response. None. The app expects a string (the STC Pay start).

Errors seen. The app 2.31.5 call above, 422 (captured on dev, 2026-10-02): {"name":"Unprocessable Content","message":"Checkout payment is not configured properly.","code":0,"status":422,"type":"Symfony\\Component\\HttpKernel\\Exception\\HttpException"}.

What is known: Locate treats STC Pay as a wallet inside the pay-session route and does not use a separate STC Pay endpoint for point orders.

POST /maeda/order/payfort-token​

No Locate replacement yet — the app keeps calling Shukah.

Full dev URL: https://dev-api.shukah.com/maeda/order/payfort-token

Warning: starts a PayFort payment. No runnable command here.

Request shape. JSON with point_order_id, device_id, digital_wallet, type: "GiftOrder" and optionally promo_code. Used for the PayFort card path and the Apple Pay path of a point gift.

Captured Shukah response. None. The app expects a payment-token response; its keys: Not documented yet.

Errors seen. None.

What is known: PayFort is out of scope for Locate point orders ("deferred by design"). While the common settings' payment value is not checkout, the app sends point-gift card, Apple Pay and STC Pay payments to PayFort, and those cannot use Locate. order-v2 has a POST /api/v2/client/payments/sdk-token route that, for type: "GiftOrder", only passes the call on to this Shukah route with the caller's headers. It is not a replacement: it still needs Shukah and a token Shukah accepts.

POST /maeda/order/payfort-stc-pay-checkout​

No Locate replacement yet — the app keeps calling Shukah.

Full dev URL: https://dev-api.shukah.com/maeda/order/payfort-stc-pay-checkout

Warning: starts a PayFort STC Pay payment. No runnable command here.

Request shape. JSON with the type GiftOrder. The other keys: Not documented yet.

Captured Shukah response. None. The app expects an STC Pay payment URL; its keys: Not documented yet.

Errors seen. None.

What is known: PayFort is out of scope for Locate point orders ("deferred by design"), so this STC Pay path keeps calling Shukah.