Entry crossings polling
Use entry crossings when you need each individual pass through an entry, for example to count how busy a doorway is or to see which way people are moving. Each crossing is one person passing through one entry, with the direction resolved for every space the entry leads into. If you only need how many people are in a space right now, use occupancy instead: entry sensors already drive its headcount.
The Entry Crossings API feature is enabled by XY Sense per organisation and can vary per building and floor. A query that covers any floor without it fails rather than leaving that floor out, so if only some floors have it, filter to them with floorIds or buildingIds. If your queries return an access-disabled error, your XY Sense representative can enable it. See Feature enablement.
1. Find the entries on a floor and the spaces they lead into
An entry is a line drawn across a doorway or opening, watched by an entry sensor. shape is the line, in centimetres from the top-left of the floor plan. entryFloorSpaces lists the spaces the entry leads into, so you can label crossings by space name and decide which entries to poll.
Example query
query EntryCrossingEntries($floorId: ID!) {
floor(id: $floorId) {
id
name
entrySensorInstallations(isActive: true) {
id
name
entries {
id
name
shape
entryFloorSpaces {
floorSpaceId
floorSpace {
name
}
}
}
}
}
}
Example variables
{
"floorId": "a2961d46-971f-4ce1-a66e-7ee7bdb2a204"
}
Example response
{
"data": {
"floor": {
"id": "a2961d46-971f-4ce1-a66e-7ee7bdb2a204",
"name": "Level 12",
"entrySensorInstallations": [
{
"id": "0c9d4b7e-2f61-4a8e-b3d5-6e1f9a2c7b40",
"name": "Lobby sensor",
"entries": [
{
"id": "5f3c8e21-7a4d-4c1b-9e62-0d8b7a1f4c35",
"name": "Meeting room 12.04 door",
"shape": [
{ "x": 1820, "y": 940 },
{ "x": 1910, "y": 940 }
],
"entryFloorSpaces": [
{ "floorSpaceId": "ac52e43d-7c21-4464-ab21-d0ace07a94b1", "floorSpace": { "name": "Meeting room 12.04" } },
{ "floorSpaceId": "7f10b2c9-5e83-4d26-a1f4-3b9c8e6d2a17", "floorSpace": { "name": "Level 12 open plan" } }
]
}
]
}
]
}
}
}
2. Fetch recent crossings
If you omit from, the API returns the last minute of crossings for the matching floors.
Example query
query RecentEntryCrossings($floorIds: [ID!]) {
entryCrossings(floorIds: $floorIds) {
nextFrom
data {
id
entryId
floorId
collectedDate
lineDirection
spaces {
floorSpaceId
direction
}
}
}
}
Example variables
{
"floorIds": [
"a2961d46-971f-4ce1-a66e-7ee7bdb2a204"
]
}
Example response
{
"data": {
"entryCrossings": {
"nextFrom": "2026-06-02T07:36:12.001Z",
"data": [
{
"id": "8e4b1c02-9d37-4f5a-b6e1-2c7a9f0d3b58",
"entryId": "5f3c8e21-7a4d-4c1b-9e62-0d8b7a1f4c35",
"floorId": "a2961d46-971f-4ce1-a66e-7ee7bdb2a204",
"collectedDate": "2026-06-02T07:36:09.412Z",
"lineDirection": "Forward",
"spaces": [
{ "floorSpaceId": "ac52e43d-7c21-4464-ab21-d0ace07a94b1", "direction": "In" },
{ "floorSpaceId": "7f10b2c9-5e83-4d26-a1f4-3b9c8e6d2a17", "direction": "Out" }
]
}
]
}
}
}
3. Read the direction of a crossing
Use spaces[].direction to tell whether a crossing went into or out of a space:
InorOutis resolved for you, per space. A door between two spaces, like the meeting room door above, isInfor one space andOutfor the other in the same crossing, so counting by space never counts a crossing twice.- It uses the mapping at the time of the crossing. If an entry is later mapped to different spaces, older crossings keep the direction they had when they happened.
spacesis empty when the entry is not mapped to any space.lineDirectionis then the only direction available.
lineDirection is Forward or Backward relative to the entry line itself. Crossings without a known direction are never recorded.
4. Poll for new crossings with a moving cursor
Persist nextFrom and send it as the next request's from. Filter with floorIds, buildingIds or entryIds to narrow the results. A floorIds filter matches crossings from sensors on that floor and crossings into spaces on that floor. Crossings are only recorded when the feature is enabled on the entry sensor's floor, so a crossing into a space on this floor from a sensor on a floor without the feature is never returned, and one from a sensor on a floor with it includes all its spaces, wherever they are.
Example query
query EntryCrossingUpdates($floorIds: [ID!], $entryIds: [ID!], $from: Date!, $to: Date) {
entryCrossings(floorIds: $floorIds, entryIds: $entryIds, from: $from, to: $to) {
from
to
nextFrom
data {
id
entryId
floorId
collectedDate
lineDirection
spaces {
floorSpaceId
direction
}
}
}
}
Example variables
{
"floorIds": [
"a2961d46-971f-4ce1-a66e-7ee7bdb2a204"
],
"entryIds": [
"5f3c8e21-7a4d-4c1b-9e62-0d8b7a1f4c35"
],
"from": "2026-06-02T07:30:00Z",
"to": "2026-06-02T07:40:00Z"
}
Example response
{
"data": {
"entryCrossings": {
"from": "2026-06-02T07:30:00Z",
"to": "2026-06-02T07:40:00Z",
"nextFrom": "2026-06-02T07:40:00.001Z",
"data": [
{
"id": "8e4b1c02-9d37-4f5a-b6e1-2c7a9f0d3b58",
"entryId": "5f3c8e21-7a4d-4c1b-9e62-0d8b7a1f4c35",
"floorId": "a2961d46-971f-4ce1-a66e-7ee7bdb2a204",
"collectedDate": "2026-06-02T07:36:09.412Z",
"lineDirection": "Forward",
"spaces": [
{ "floorSpaceId": "ac52e43d-7c21-4464-ab21-d0ace07a94b1", "direction": "In" },
{ "floorSpaceId": "7f10b2c9-5e83-4d26-a1f4-3b9c8e6d2a17", "direction": "Out" }
]
}
]
}
}
}
Things to handle when polling:
fromandtoare when a crossing reached XY Sense, notcollectedDate. Crossings a sensor sends late, for example after a network outage, appear in a later page than theircollectedDatesuggests. Always page withnextFromrather than computing the next window fromcollectedDate.- The same crossing can be returned more than once. Drop duplicates using
id, which stays the same for a given crossing. - Delivery is best effort. A crossing can occasionally be missing, so treat counts as close estimates rather than an exact audit.
- Polling again within a second of
nextFromreturns an empty page with the samenextFrom. Wait and try again.
Request windows, retention and rate limits are in Limits and retention and Limits, versioning, and support.