Point offers
Background
The point gift form needs two lists: the point packages (offers) and the occasions. On Shukah the app
reads them with GET /maeda/point-offer and GET /maeda/point-occasion. Locate has the same two
routes, and one new route that returns a single offer. The fields are the same as Shukah's. Three
things need care: the id of an offer and of an occasion is a 24-character string instead of a
number, Locate needs a token where Shukah answered without one, and Shukah's ?id= returns the whole
list.
Feature
An offer is a package of points that a client can buy with a card or pay for with points, to send to another client. An occasion is the reason for the gift, such as Birthday. In the app snapshot of 2026-02-26 the calls happen on the send-gift form:
| Call | Where in the app |
|---|---|
| Offers list | When the gift-amount picker appears on the send-gift form. |
One offer (Shukah ?id=) | In the gift summary, in the checkout button and in the payment-method picker. The app asks Shukah for ?id= and then picks the offer out of the returned list itself. |
| Occasions list | When the occasion picker appears on the send-gift form. |
One occasion (Shukah ?id=) | In the gift summary. The same client-side pick. |
The ids go into the later calls. The app sends the offer id as point_offer_id and the occasion
id as point_occasion_id when it asks for the message text
(Point order description) and when it creates the order
(Create point order).
All of these need a token on Locate, and on dev today only a Shukah token works: a Locate JWT got
401 Invalid or expired token on the offers and occasions lists on 2026-10-02, although both
tokens worked on the balance route on 2026-10-01 (see
Which tokens work). order-v2 passes each call to the point
service and returns the answer unchanged.
Ids: store them, send them back, never parse them. An offer or occasion id is the 24-character
hex string of its database row. It is different in every environment, so the app must read ids from
the list and never write one in code. A Shukah integer sent to Locate in place of an id is refused
with 400. The app already turns ids into text (String(id)) and compares them as text, which works
with the new ids, but its TypeScript types still say number.
The is_custom rule. is_custom is 0 or 1, never a boolean. When it is 1 (on dev:
Thank and Other) the app shows a box for free text and sends what the client typed as
occasion, next to the occasion id.
GET /api/v2/client/point-offers
GET /maeda/point-offer✅ Replaced- 🟡 id is a 24-hex string (Shukah 1..5); the app does String(id), so it works; a Shukah id gives 400
- 🟡 Locate needs a token; Shukah answered 200 with none; the app always sends one
- 🟡 Locate JWT gets 401 on dev; send the Shukah token
- 🟡 The five offers are equal on point_amount, purchased_point_amount, amount ("40.00" string) and order
The ?id= filter is ignored: the call still returns the whole list.
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, on the two lists): see Which tokens work. | The client's own token. |
id | query | any | no | Accepted and ignored: the whole list is always returned, as Shukah's ?id= does. | Kept so the old Shukah call still works. |
| Any other query key | query | — | no | Accepted and ignored, for example language. | — |
Response
200 with a bare array of the active offers. It can be empty. There is no paging.
| Field | Type | Nullable | Format / constraints | Description |
|---|---|---|---|---|
id | string | no | A 24-character hex string, for example "6a46104f3dd7b3dd6a261d90". Different in every environment. Shukah sends an integer. | The offer id. Send it back as point_offer_id. |
point_amount | number | no | A JSON number. | The points the recipient gets. |
purchased_point_amount | number | yes | null when the offer has no points price. On dev it is higher than point_amount. | The points the sender pays when paying with points. |
amount | string | no | Exactly two decimals, for example "40.00". | The price in SAR for a card payment. |
[
{"id": "6a46104f3dd7b3dd6a261d90", "point_amount": 100, "purchased_point_amount": 110, "amount": "40.00"},
{"id": "6a46104f3dd7b3dd6a261d91", "point_amount": 200, "purchased_point_amount": 240, "amount": "60.00"},
{"id": "6a46104f3dd7b3dd6a261d94", "point_amount": 300, "purchased_point_amount": 360, "amount": "90.00"},
{"id": "6a46104f3dd7b3dd6a261d96", "point_amount": 400, "purchased_point_amount": 480, "amount": "120.00"},
{"id": "6a46104f3dd7b3dd6a261d98", "point_amount": 500, "purchased_point_amount": 600, "amount": "150.00"}
]
Captured on dev, 2026-10-01 (client 356259): all five offers.
The sender pays more points than the recipient gets: 110 for 100, 240 for 200, 360 for 300, 480 for
400 and 600 for 500. An offer with purchased_point_amount of null cannot be paid with points:
create point order answers 400 for it.
Order. The offers come sorted by an admin-set sequence number, then by the Shukah id, then by the row id. That sort was committed on 2026-10-01. Whether dev ran it when the sample was taken: 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 on the two lists. |
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/point-offers" -H "Authorization: Bearer $SHUKAH_TOKEN"
Expected: 200 and the array 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 five offers are equal on point_amount, purchased_point_amount, amount and key order on
2026-10-01 and again on 2026-10-02. Only id differs.
| Aspect | Shukah | Locate | Impact on the app |
|---|---|---|---|
| URL | GET $SHUKAH_BASE_URL/maeda/point-offer | GET $ORDER_BASE_URL/api/v2/client/point-offers | 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 offers before login would fail. Whether such a screen exists: Not documented yet. |
id | An integer, 1 to 5 (captured on dev, 2026-10-01). | A 24-character string. | The app turns ids into text, so it works. Its types say number. Never mix the two kinds: Locate refuses a Shukah integer. |
?id= | Returns the whole list. The app picks the offer out itself. | Accepted and ignored: the whole list again. | None: the app's client-side pick works on the new ids. |
purchased_point_amount | A number (captured on dev, 2026-10-01). | A number, or null when the offer has no points price. | The app types it as a number and copies it to costInPoints. A null has to be handled. |
| Order | Not documented yet. | By an admin-set sequence number, then Shukah id, then row id. | If the app shows the list in server order, the order can change. |
| 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. |
GET /api/v2/client/point-offers/:id
New route. Shukah has no route that returns one offer: its ?id= returns the whole list, and the app
picks the offer out of that list itself. The app does not call this route today.
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, on the two lists): see Which tokens work. | The client's own token. |
id | path | string | yes | Exactly 24 hex characters (upper or lower case). A Shukah integer is refused, not looked up. | The offer id from the list. |
| Any query key | query | — | no | Forwarded by order-v2 and ignored by the point service. | — |
Response
200 with one object, the same shape as a row of the list (id, point_amount,
purchased_point_amount, amount; see the list).
{"id": "6a46104f3dd7b3dd6a261d90", "point_amount": 100, "purchased_point_amount": 110, "amount": "40.00"}
Captured on dev, 2026-10-01 (client 356259): the first offer.
Errors
| HTTP | Body | When |
|---|---|---|
400 | {"success":false,"error":{"code":"VALIDATION_ERROR","message":"point offer id must be a 24-character hex ObjectId"}} | :id is not 24 hex characters. There is no details key. Not captured. |
404 | {"success":false,"error":{"code":"NOT_FOUND","message":"point offer <id> not found or inactive"}} | No offer with that id, or it is inactive or deleted. A 24-hex id that does not exist is a 404, not a 400. Not captured. |
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 on the two lists. |
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. |
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
export OFFER_ID='<id-from-the-offers-list>'
curl -s "$ORDER_BASE_URL/api/v2/client/point-offers/$OFFER_ID" -H "Authorization: Bearer $SHUKAH_TOKEN"
Expected: 200 and the offer shown under Response. An id that is not 24 hex characters gives the 400
shown under Errors. Use a Shukah token on dev today: this route has the same token check as the lists.
GET /api/v2/client/point-occasions
GET /maeda/point-occasion✅ Replaced- 🟡 id is a 24-hex string (Shukah 1..7); the app does String(id); a Shukah id sent to Locate create is 400
- 🟡 Locate needs a token; Shukah answered 200 with none; the app always sends one
- 🟡 Locate JWT gets 401 on dev; send the Shukah token
- 🟡 Seven rows are equal on occasion, occasion_en, is_custom (number 0/1) and key order
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, on the two lists): see Which tokens work. | The client's own token. |
language | query | string | no | After trimming and lower-casing, exactly ar gives the Arabic label. Any other value, including ar-SA and en-GB, and no value, gives English. Never an error. | The language of the occasion label. |
id | query | any | no | Accepted and ignored: the whole list is returned. | Kept so the old Shukah call still works. |
The app adds language=en-GB or language=ar-SA to every call, so with the app's own values the
label is always English. Whether that is intended: Not documented yet.
Response
200 with a bare array of the active occasions. It can be empty. There is no paging.
| Field | Type | Nullable | Format / constraints | Description |
|---|---|---|---|---|
id | string | no | A 24-character hex string. Different in every environment. Shukah sends an integer. | The occasion id. Send it back as point_occasion_id. |
occasion | string | no | The English label, trimmed. With language=ar, the Arabic label, or the English one when there is no Arabic. | The label to show. |
occasion_en | string | no | The English label, trimmed, always. | The English label. |
is_custom | integer | no | 0 or 1, never a boolean. | 1 means the app shows a box for free text. |
[
{"id": "6a4610503dd7b3dd6a261dbf", "occasion": "Birthday", "occasion_en": "Birthday", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc0", "occasion": "Born", "occasion_en": "Born", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc1", "occasion": "Gift", "occasion_en": "Gift", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc3", "occasion": "Wedding", "occasion_en": "Wedding", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc5", "occasion": "Graduation", "occasion_en": "Graduation", "is_custom": 0},
{"id": "6a4610503dd7b3dd6a261dc6", "occasion": "Thank", "occasion_en": "Thank", "is_custom": 1},
{"id": "6a4610503dd7b3dd6a261dc7", "occasion": "Other", "occasion_en": "Other", "is_custom": 1}
]
Captured on dev, 2026-10-01 (client 356259), with no language: all seven occasions.
The app has a type problem that exists on Shukah too: it reads the English label as occasionEn,
but both servers send occasion_en, so the app's nameEn is undefined today.
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 on the two lists. |
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. |
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/point-occasions?language=en-GB" -H "Authorization: Bearer $SHUKAH_TOKEN"
Expected: 200 and the array 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 seven rows are equal on occasion, occasion_en, is_custom and key order on 2026-10-01 and
again on 2026-10-02. Only id differs.
| Aspect | Shukah | Locate | Impact on the app |
|---|---|---|---|
| URL | GET $SHUKAH_BASE_URL/maeda/point-occasion | GET $ORDER_BASE_URL/api/v2/client/point-occasions | 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. |
id | An integer, 1 to 7 (captured on dev, 2026-10-01). | A 24-character string. | The app turns ids into text, so it works. Never mix the two kinds. |
?id= | Returns the whole list. | Accepted and ignored: the whole list again. | None: the app's client-side pick works on the new ids. |
occasion label | The same English text in occasion and occasion_en (captured on dev, 2026-10-01). | English, or Arabic when language is exactly ar. | The app sends ar-SA, so it never gets the Arabic label. |
| Order | Not documented yet. | By an admin-set sequence number, then Shukah id, then row id. | If the app shows the list in server order, the order can change. |
| 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. |
Watch out
- Offer and occasion ids are 24-character strings that differ per environment: store them, send them back, never parse them, and never send a Shukah integer.
- Locate needs a token on all three routes, where Shukah's lists were public.
?id=still returns the whole list, so pick the offer or occasion out of the array, or useGET /point-offers/:id.
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.
Routes and proxy
| App route (order-v2) | Upstream route (point service) | Forwarded | Limit |
|---|---|---|---|
GET /api/v2/client/point-offers | GET /api/client/point-offers | Authorization and the whole query | 10 s |
GET /api/v2/client/point-offers/:id | GET /api/client/point-offers/<id>, with the id passed through encodeURIComponent | Authorization and the whole query (ignored upstream) | 10 s |
GET /api/v2/client/point-occasions | GET /api/client/point-occasions | Authorization and the whole query, so language arrives | 10 s |
(OV2: src/modules/client/point/point.controller.ts:61-108,
PS: services/api/src/routes/client.routes.ts:20-22,
PS: services/api/src/handlers/client-point-offers.ts:12-23,
PS: services/api/src/handlers/client-point-occasions.ts:12-14.)
What the point service reads
- Offers. Rows of
point_offerswithstatus: 'active'anddeleted_at: null, sorted bysequence, thenshukah_id, then_id. There is no paging or limit. A Locate-made offer with no Shukah id is listed like any other (PS: services/api/src/services/point-settings.service.ts:10-25). The sort was changed on 2026-10-01 (commit105119c, merged 14:25 +07), while the integration doc still says "ordered byshukah_id" (PS: docs/plans/2026-10-01-app-language-and-arabic-reason.md:6-8,PS: docs/integrations/2026-07-16-client-facing-point-apis.md:321-322). The captures were taken at about 17:19 +07. Whether that build was on dev: Not documented yet. - One offer. The same filter on
_id. An inactive or deleted offer is404(PS: services/api/src/services/point-settings.service.ts:29-34). The id check is^[0-9a-fA-F]{24}$(PS: services/api/src/lib/object-id.ts:12-14,PS: services/api/src/handlers/client-point-offers.ts:16,19-23). - Occasions. Rows of
point_occasionswith the same filter, sorted the same way (PS: services/api/src/services/point-settings.service.ts:38-42). Thelanguagerule (exactlyarafter trim and lower-case) shipped in the same commit105119c(PS: services/api/src/handlers/client-point-occasions.ts:5-8,PS: docs/plans/2026-10-01-app-language-and-arabic-reason.md:10-17). - Ids. The ids are the rows' own
_idvalues, rendered as 24-character lowercase hex. Uppercase is accepted on input (PS: services/api/src/services/point-settings.mappers.ts:25-58). The integers that Shukah uses are still stored on the rows asshukah_id, but the client routes no longer accept them: apoint_offer_idthat is not 24 hex characters fails withmust be a 24-character hex ObjectId(checked with a local run of the schema, zod 3.25.76). - The
?id=query. The handler reads noid; the whole list is returned, which the point service doc says matches Shukah, "measured on dev 2026-09-21" (PS: services/api/src/handlers/client-point-offers.ts:7-11,PS: docs/integrations/2026-07-16-client-facing-point-apis.md:324-328). is_custom. The mapper writesdoc.is_custom ? 1 : 0(PS: services/api/src/services/point-settings.mappers.ts:56). A custom occasion is the one case where the app sends free text inoccasiontogether with the id (PS: services/api/src/services/point-settings.service.ts:57-58).- Edits. Offers and occasions are changed through admin routes under
/api/admin/…with a service key, never by the app (PS: services/api/src/routes/admin.routes.ts:34-44).
The app
The app's types say id: number for offers and occasions
(mobile/locate-client@e960c3a: src/entities/points/model/types/pointGiftOption.server.ts:1-6,
mobile/locate-client@e960c3a: src/entities/points/model/types/pointGiftOccasion.server.ts:1-6). The
DTOs turn the id into text and compute cost as Number(amount), costInPoints as
purchased_point_amount, and isCustom as String(is_custom) === '1'
(mobile/locate-client@e960c3a: src/entities/points/model/api/dto/points.dto.ts:104-136). The by-id
helpers filter the returned list with String(item.id) === request.id
(mobile/locate-client@e960c3a: src/entities/points/model/api/services/points.service.ts:212-241,285-295).
The language interceptor adds language=en-GB or language=ar-SA to every call
(mobile/locate-client@e960c3a: src/shared/api/interceptors/languageInterceptor.ts:5-15).
Where the docs and the code disagree (code wins)
- The integration doc says offers are ordered by
shukah_idwith_idas the tiebreak (PS: docs/integrations/2026-07-16-client-facing-point-apis.md:321-322). The code sorts bysequencefirst. - 2026-10-02: this page said a Shukah token and a Locate JWT both worked on these routes. On
2026-10-02 the JWT got
401on the offers and occasions lists, and the Try it calls now use 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:61-108locate-point-service@2c44ce3:services/api/src/routes/client.routes.ts:20-22,services/api/src/handlers/client-point-offers.ts:7-23,services/api/src/handlers/client-point-occasions.ts:5-14,services/api/src/services/point-settings.service.ts:10-42,57-58,services/api/src/services/point-settings.mappers.ts:25-58,services/api/src/lib/object-id.ts:12-14,services/api/src/routes/admin.routes.ts:34-44locate-point-service@2c44ce3docs:docs/integrations/2026-07-16-client-facing-point-apis.md:321-328,docs/plans/2026-10-01-app-language-and-arabic-reason.md:6-17- 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:190-263,285-295,src/entities/points/model/api/dto/points.dto.ts:104-136,src/entities/points/model/types/pointGiftOption.server.ts:1-6,src/entities/points/model/types/pointGiftOccasion.server.ts:1-6,src/features/SelectPointGift/model/hooks/useSelectPointGiff.ts:16,src/features/SelectPointGiftOccasion/model/hooks/useSelectPointGiftOccasion.ts:10,src/widgets/SendPointGiftForm/ui/SendPointGiftForm/SendPointGiftForm.tsx:149-185,src/features/PointGiftSummary/ui/PointGiftSummary/PointGiftSummary.tsx:34-37,src/features/PointGiftsCheckoutButton/ui/PointGiftsCheckoutButton/PointGiftsCheckoutButton.tsx:33,src/features/SelectPointGiftPaymentMethod/model/hooks/useSelectPointGiftPaymentMethod.ts:52,src/shared/api/interceptors/languageInterceptor.ts:5-15 - Dev captures, 2026-10-02 (parity audit):
point-offer--shukah,point-offer--locate-st,point-offer--locate-jwt,point-occasion--shukah,point-occasion--locate-st,point-occasion--locate-jwt - Dev captures, 2026-10-01:
locate-point-offers,locate-point-offers-again,locate-point-offer,locate-point-occasions,locate-point-occasions-again,shukah-point-offer,shukah-point-occasion,probe-locate-point-offers,probe-locate-point-offer,probe-locate-point-occasions,probe-shukah-point-offer,probe-shukah-point-occasion