Using Store and Facility Data
Stores are your physical or virtual locations; facilities are amenities a store can offer (parking, drive-through, kids area). Both are project-level reference data. Storelayer does not attach a store to a customer, event or cart by itself: your app sends the storeId with each cart or event, and promotions and rules compare against it.
Store fields
Section titled “Store fields”POST /projects/{projectId}/stores
{ "id": "ist-kadikoy", "name": "Kadıköy", "status": "active", "countryCode": "TR", "location": { "city": "Istanbul", "postalCode": "34710", "lat": 40.9903, "lng": 29.0275, "formattedAddress": "Caferağa Mah., Kadıköy" }, "phone": "+902161234567", "workingHours": { "mon": { "open": "08:00", "close": "22:00" }, "sun": null }, "facilityIds": ["fac_k2m9x7"], "tags": ["flagship", "airport-adjacent"], "metadata": { "posCode": "K01", "region": "anatolia" }}Only name is required. Send your own id (your POS or ERP store code): it is the value carts and events will carry. Without one Storelayer generates store_…. status is active (default), inactive or temporarily_closed. workingHours keys are mon…sun; null means closed. Also available: description, email, managerId, imageUrl (or upload with PUT /stores/{storeId}/image).
GET /projects/{projectId}/stores?status=active&countryCode=TR&limit=100 lists stores oldest first, as { "items": [...], "total": n }.
Updating
Section titled “Updating”PUT /projects/{projectId}/stores/{storeId} changes only the top-level fields you send, but each object or array you send replaces the stored value: sending metadata, location, tags, facilityIds or workingHours overwrites it entirely. Read the store first and send the complete object.
Bulk import
Section titled “Bulk import”POST /projects/{projectId}/stores/import
{ "stores": [ { "id": "ist-kadikoy", "name": "Kadıköy" }, { "id": "ank-cankaya", "name": "Çankaya" } ] }Returns { "created": n, "skipped": n, "errors": [{ "index": 3, "id": "…", "message": "…" }] }. Stores whose id already exists are skipped, not updated, so the import is safe to re-run. To change existing stores, use PUT per store. Imports without ids create a new store on every run.
Facilities
Section titled “Facilities”POST /projects/{projectId}/facilities with { "name": "Parking", "description": "Free for 2 hours", "icon": null } returns a facility with a generated id. Link facilities by putting their IDs in a store’s facilityIds. Storelayer does not check those links: deleting a facility leaves its ID in stores’ facilityIds, so remove it from stores yourself.
Location in promotions
Section titled “Location in promotions”The cart sent to POST /projects/{projectId}/promotions/evaluate accepts storeId (plus salesChannel and region). Conditions read it as {{ $('cart').storeId }}:
{ "combinator": "OR", "conditions": [ { "leftValue": "{{ $('cart').storeId }}", "operator": "eq", "rightValue": "ist-kadikoy", "rightType": "string" }, { "leftValue": "{{ $('cart').storeId }}", "operator": "eq", "rightValue": "ank-cankaya", "rightType": "string" } ]}A cart without storeId has storeId: null and fails these conditions, which is the safe default for an in-store-only offer. For “everywhere except store X”, combine neq with an exists check so online carts without a store don’t qualify by accident (or do, if that is intended).
Location in rules
Section titled “Location in rules”Events carry whatever your app puts in payload, for example "payload": { "orderId": "ord_1", "amount": 4599, "storeId": "ist-kadikoy" }. Compare it directly with {{ $('event').payload.storeId }}. To use store attributes (tags, status, metadata), register an internal resource and declare it on the rule:
{ "key": "store", "name": "Purchase store", "type": "internal", "config": { "toolName": "stores.get_store", "params": { "storeId": "{{ $('event').payload.storeId }}" } }, "dependsOn": ["event"]}Then {{ $('store').tags }} contains "airport-adjacent" awards only at tagged stores. An unknown or missing storeId resolves to an object with only _error, so field conditions fail rather than match.
Verify
Section titled “Verify”Test an eligible store, an excluded store, and a request with no storeId, using promotions/evaluate (dry run) for promotions and POST /projects/{projectId}/rules/test for rule conditions. Before deleting or renaming a store id, search promotions and rules for that value: conditions compare strings and do not follow renames.