Points FAQ
Background
On Shukah the app reads the points questions and answers with GET /maeda/points-faq. Locate's
GET /api/v2/client/points-faq returns the same list, and on dev the five items were identical to
Shukah's on 2026-10-01 and again on 2026-10-02, key for key. The one thing that changes is the
token: Locate needs one, and on dev today it must be a Shukah token, while Shukah's route answered
without one.
Feature
The FAQ is a list of questions and answers about points: what a point is worth, how to earn points, how to use them. It is the same list for every client. In the app snapshot of 2026-02-26 the call happens once, when the Wallet screen shows its FAQ section. The app shows nothing when the list is empty.
Every item carries both languages: title_ar, text_ar, title_en and text_en. The server
always sends all four, and the app chooses which pair to show. language is not read by the
point service, and order-v2 does not forward a query on this route, so the language the app adds
to every call is dropped.
Only a Shukah token works on dev today: a Locate JWT got 401 Invalid or expired token on this
route on 2026-10-02, although both tokens worked on the balance route on 2026-10-01 (see
Which tokens work). order-v2 passes the call to the point
service and returns the answer unchanged.
GET /api/v2/client/points-faq
GET /maeda/points-faq✅ Replaced- 🟡 Locate needs a token; Shukah answered 200 with none; the app always sends one
- 🟡 Locate JWT gets 401 on dev; send the Shukah token
- 🟡 Five items deep-equal incl. \r\n; the FAQ's earn text matches neither rate table
The five FAQ entries are identical to Shukah on dev, but Locate needs a token where Shukah answered without one.
Request
| Field | In | Type | Required | Constraints | Description |
|---|---|---|---|---|---|
Authorization | header | string | yes | Bearer <token>: a Shukah token on dev today. A Locate JWT gets 401 Invalid or expired token (captured on dev, 2026-10-02): see Which tokens work. | The client's own token. |
language | query | string | no | Accepted and ignored: all four fields are always returned. order-v2 does not forward it. | Kept so the old Shukah query string still works. |
Response
200 with a bare array. The keys of each item come in this order: title_ar, text_ar, title_en,
text_en. The array follows the order set in the FAQ settings and holds only the active items. On dev it had five items.
| Field | Type | Nullable | Format / constraints | Description |
|---|---|---|---|---|
title_ar | string | no | "" when missing. | The Arabic question. |
text_ar | string | no | "" when missing. Has \r\n line breaks. | The Arabic answer. |
title_en | string | no | "" when missing. | The English question. |
text_en | string | no | "" when missing. Has \r\n line breaks. One live item ends with \t \r\n. | The English answer. |
[
{
"title_ar": "كم قيمة النقطة في لوكيت؟",
"text_ar": "النقطة الواحدة تساوي 0.25 ريال.\r\nيعني 4 نقاط تساوي ريال واحد.",
"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 (client 356259): the first of five items.
The answer to "How I can earn points?" states an earn table with 35–69 SR for 1 point, 70–139 SR for 2, 140–250 SR for 4, 251–450 SR for 8 and 451 SR or more for 16. That matches neither earn-rate table captured the same day (see Points wallet). Which table is the right one: Not documented yet.
Errors
| HTTP | Body | When |
|---|---|---|
401 | {"success":false,"error":{"code":"UNAUTHORIZED","message":"Missing or invalid authorization header"}} | There is no Authorization header, or it does not start with Bearer . Captured on dev, 2026-10-01, with no token. |
401 | The same shape, with message Invalid or expired token, auth service unreachable: <error text> or auth response missing numeric client id. | The auth service refused the token, could not be reached (an auth outage looks like a bad token), or answered without a client id. Invalid or expired token was captured on dev, 2026-10-02, with a Locate JWT. |
500 | {"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}} | Any unexpected failure in the point service, for example a database error. The message is AUTH_SERVICE_URL not set when the service is misconfigured. Not captured. |
502 | {"success":false,"error":{"code":"POINT_SERVICE_UNAVAILABLE","message":"Point service unavailable: <error text>"}} | order-v2 could not reach the point service. This body comes from order-v2, not from the point service. Not captured. |
504 | {"success":false,"error":{"code":"POINT_SERVICE_TIMEOUT","message":"Point service did not respond in time; the request outcome is unknown"}} | The point service did not answer within 10 seconds. This body comes from order-v2. Not captured. |
Every error from the point service has the envelope {"success":false,"error":{"code":"…","message":"…"}}.
Try it
export ORDER_BASE_URL=https://dev.v2.api.order.locate.sa
export SHUKAH_TOKEN='<shukah-token>' # a Locate JWT gets 401 here today; see "Which tokens work" on Points wallet
curl -s "$ORDER_BASE_URL/api/v2/client/points-faq" -H "Authorization: Bearer $SHUKAH_TOKEN"
Expected: 200 and an array of five items like the one shown under Response. Use a Shukah token on
dev today: a Locate JWT gets 401 Invalid or expired token (2026-10-02). With no token you get the
401 shown under Errors.
Differences from Shukah
The two five-item arrays were deep-equal on 2026-10-01 and on 2026-10-02, with the same key order
and the same \r\n line breaks. The rows below cover the token, the URL and the error body.
| Aspect | Shukah | Locate | Impact on the app |
|---|---|---|---|
| URL | GET $SHUKAH_BASE_URL/maeda/points-faq | GET $ORDER_BASE_URL/api/v2/client/points-faq | Change the base URL and the path. |
| Token | None needed: 200 with no token (captured on dev, 2026-10-01). | Required: 401 with no token (captured on dev, 2026-10-01). A Shukah token only on dev today: a Locate JWT gets 401 (2026-10-02). | The app already sends its token on every call, and must keep it a Shukah token here. A screen that shows the FAQ before login would fail. Whether such a screen exists: Not documented yet. |
| Error body | Not documented yet. The route is public, so there is no 401 to compare. | The point-service envelope under Errors. | Anything that matches on error text must change. |
Content-Type | application/json | application/json; charset=utf-8 | None. |
Watch out
- Locate needs a token on this route, where Shukah's answered without one.
- All four fields come back in every item, and
languageis ignored, so the app has to pick the Arabic or English pair itself. - The earn table written in the FAQ text matches neither earn-rate table on dev.
Details (for backend)
Only dev was checked, through order-v2 at https://dev.v2.api.order.locate.sa, prefix /api/v2/client.
UAT and production hosts: Not documented yet. In this section OV2: means
locate-order-v2-api@d9e4e5f6 and PS: means locate-point-service@2c44ce3. How the proxy, the
token check and the error envelope work is described once, under
How the proxied calls work.
Route and proxy
GET /api/v2/client/points-faq goes to GET /api/client/points-faq on the point service. order-v2
forwards Authorization only, with a 10 second limit, and no query
(OV2: src/modules/client/point/point.controller.ts:47-59,
PS: services/api/src/routes/client.routes.ts:19,
PS: services/api/src/handlers/client-points-faq.ts:6-9).
What the point service reads
It reads point_faqs rows with status: 'active', sorted by ordering from low to high, and
flattens each {title:{en,ar}, text:{en,ar}} document into the four flat keys. A missing value
becomes "" (PS: services/api/src/services/client-points.service.ts:43-50,
PS: services/api/src/services/client-points.mappers.ts:58-71; tests
PS: services/api/src/services/client-points.mappers.test.ts:88-107). The list is not specific to the
client. The auth service's ETL fills point_faqs from Shukah's FAQ table; the client routes cannot
write it (PS: docs/integrations/2026-07-16-client-facing-point-apis.md:172-174,
PS: docs/2026-07-10-local-testing.md:53).
Parity
On 2026-10-01 the Locate and Shukah arrays were compared by program and were deep-equal, with the same
key order. In July the Locate list was an empty stub, so this closes that finding
(PS: docs/reports/2026-07-17-client-api-parity.md:165-168). The point service doc already
recorded equality after the 2026-07-17 implementation
(PS: docs/integrations/2026-07-16-client-facing-point-apis.md:171-174).
The app
The app turns each item into a title and body in English and in Arabic, shows the pair that matches
the app language, uses the English title as the list key, and shows nothing for an empty list
(mobile/locate-client@e960c3a: src/entities/points/model/api/dto/points.dto.ts:53-68,
mobile/locate-client@e960c3a: src/features/PointsFaq/ui/FaqItem/FaqItem.tsx:32-37,
mobile/locate-client@e960c3a: src/features/PointsFaq/ui/PointsFaq/PointsFaq.tsx:27-70). It calls the
route with no query of its own (mobile/locate-client@e960c3a: src/entities/points/model/api/services/points.service.ts:62-76).
Where the docs and the code disagree (code wins)
- 2026-10-02: this page said a Shukah token and a Locate JWT both worked here. On 2026-10-02 the JWT
got
401Invalid or expired token, and the Try it now uses a Shukah token (PS: services/api/src/services/client-auth.service.ts:22-29; see Which tokens work).
Sources
locate-order-v2-api@d9e4e5f6:src/modules/client/point/point.controller.ts:47-59locate-point-service@2c44ce3:services/api/src/routes/client.routes.ts:19,services/api/src/handlers/client-points-faq.ts:6-9,services/api/src/services/client-points.service.ts:43-50,services/api/src/services/client-points.mappers.ts:58-71,services/api/src/services/client-points.mappers.test.ts:88-107locate-point-service@2c44ce3docs:docs/integrations/2026-07-16-client-facing-point-apis.md:171-174,docs/2026-07-10-local-testing.md:53,docs/reports/2026-07-17-client-api-parity.md:165-168- Mobile snapshot
mobile/locate-client@e960c3a(branchdevelopment, 2026-02-26; the app's own code, not Locate):src/entities/points/model/api/services/points.service.ts:62-76,src/entities/points/model/api/dto/points.dto.ts:53-68,src/features/PointsFaq/ui/PointsFaq/PointsFaq.tsx:27-70,src/features/PointsFaq/ui/FaqItem/FaqItem.tsx:32-37,platforms/mobile/products/locate/screens/home/mainInner/Wallet/ui/Wallet/Wallet.tsx:84 - Dev captures, 2026-10-02 (parity audit):
points-faq--shukah,points-faq--locate-st,points-faq--locate-jwt,points-faq-notoken--shukah - Dev captures, 2026-10-01:
locate-points-faq,shukah-points-faq,probe-locate-points-faq,probe-shukah-points-faq