Stores & locations
Your app can record when a customer is in one of your stores. The phone sends its position, we find the nearest active store, and if the customer is close enough a visit starts. Visits can earn rewards through the automations you set up in Admin.
Who calls these#
Every call here needs a signed-in customer's session token in Authorization: Bearer and acts on that customer only; see Sessions & sign-in. A customer has at most one open visit at a time.
How a visit is matched to a store#
- We take the nearest active store that has coordinates. The customer must be within 150 metres of it, unless your app is set to another radius.
- The position must be precise enough: an
accuracyworse than 100 metres is refused, unless your app is set otherwise. Ask the phone for a precise fix before checking in. - When too far away, the error tells you the nearest store and the distance, so the app can say, for example, that the customer is 300 metres from the Novi Sad store.
- An app without any active store that has coordinates records the visit at its default store, wherever the customer is. Give your stores coordinates in Admin.
The visit object#
| Field | Description |
|---|---|
| idinteger | Our id of the visit. |
| app_location_id, app_location_name | The store. |
| entry_timedatetime · ISO 8601, UTC | When the visit started. |
| leaving_timedatetime · nullable | When it ended. null while it is open. |
| duration_minutesinteger · nullable | How long it lasted, once it has ended. |
| customer_id, data_part, time_part | The customer. |
Check in, leave, list visits#
POST
/api/v2/customer/attendance
| Call | Description |
|---|---|
| POST /customer/attendance | Starts a visit. Body: latitude (−90 to 90), longitude (−180 to 180) and accuracy in metres, all required numbers. Returns data.visit and data.visits_this_month, the customer's count for the current month. |
| POST /customer/attendance/leave | Ends the open visit. Returns data.visit with its leaving_time. |
| GET /customer/attendances | The customer's visits, newest first, in data.visits, and the open one in data.active or null. Optional from and until as YYYY-MM-DD; by default the current month up to today, at most 366 days. |
Errors
All are 422, with the key in errors.AttendanceLogs and details in errors.fields.
| Key | When |
|---|---|
| API.AttendanceLogs.CustomerRequired | The session has no customer. |
| API.AttendanceLogs.PositionRequired | A coordinate is missing or out of range. |
| API.AttendanceLogs.Inaccurate | The fix is not precise enough. fields: accuracy_m, max_accuracy_m. |
| API.AttendanceLogs.OutOfRange | No store is close enough. fields: distance_m, radius_m, and the nearest store's app_location_id, app_location_name, latitude and longitude. |
| API.AttendanceLogs.NoLocation | The app has no store to record the visit at. |
| API.AttendanceLogs.AlreadyActive | A visit is already open. fields.visit is that visit. |
| API.AttendanceLogs.NotActive | Leave was called with no open visit. |
| API.AttendanceLogs.InvalidPeriod | The from–until range is not valid or longer than 366 days. |
POST/customer/attendance
curl -X POST "https://api.morffeus.com/api/v2/customer/attendance" \ -H "Authorization: Bearer $SESSION_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "latitude": 45.2551, "longitude": 19.8452, "accuracy": 12 }'
Response200 OK
{
"data": {
"visit": {
"id": 7712,
"app_location_id": 31,
"app_location_name": "Novi Sad centre",
"entry_time": "2026-10-01T10:15:00",
"leaving_time": null,
"duration_minutes": null,
…
},
"visits_this_month": 3
}
}
Error422 Unprocessable Entity
{
"errors": {
"AttendanceLogs": ["API.AttendanceLogs.OutOfRange"],
"fields": {
"code": "API.AttendanceLogs.OutOfRange",
"distance_m": 312,
"radius_m": 150,
"app_location_name": "Novi Sad centre",
…
}
}
}
Related#
- Sessions & sign-in: the session token these calls carry.
- Locations: how your systems send stores and their coordinates.
Last updated 1 October 2026 · API v2
Something wrong on this page?