This document outlines the GraphQL Occupancy API V2, which uses the Occupancy Aggregator service. Key updates include support for zone and floor queries, label aggregation, hybrid parking types, and two types of real-time subscriptions. The API is designed for efficient real-time parking space data retrieval and management, supporting various use cases from individual space monitoring to group and zone-level occupancy tracking.
Authorization
In order to authorize with this API, you need to add the following header to your request:
Key | Value |
---|---|
Authorization | <your token> |
This API is available at the following url:
https://occupancy.api.nwave.io/graphql
Your token can be obtained from the Company Info section of the Nwave’s console.
You are able to call API not more frequent, than 300 times per 5 minutes interval with use of the same token. If amount of requests exceeds the limit, API reponds with error until the end of 5 minutes interval.
Objects/Types
In GraphQL, an object is a type that defines the structure of data returned by the API. Each field can represent a specific piece of data or a link to other objects. When a client queries the API, they can specify which fields of the object they want to include in the response, allowing for customized and efficient data retrieval.
Occupancy Summary
type OccupancySummary { total: Int occupied: Int available: Int undefined: Int }
The OccupancySummary object provides an overview of parking availability at various levels of the system hierarchy, including Group, Level, Zone, and Project. It summarizes key metrics like the number of available, occupied, and total spaces to give a clear picture of the current usage and capacity at each level.
Location
type Location { lat: Float! lon: Float! }
The Location object represents the geographical coordinates (latitude and longitude) of a specific point, used to define the position of parking space.
Project Occupancy
type ProjectOccupancy { id: ID! name: String location: Location occupancy: OccupancySummary! }
The ProjectOccupancy object provides a summary of parking availability and usage across an entire project. It includes details about the project's location and name, giving an overall occupancy.
Zone Occupancy
type ZoneOccupancy { id: ID! name: String projectId: String! location: Location occupancy: OccupancySummary! }
The ZoneOccupancy object gives a summary of parking availability within a specific zone. It includes the zone's location, name, and project association to provide a focused view of parking status within that area.
Level Occupancy
type LevelOccupancy { id: ID! name: String zoneId: Int! projectId: Int! floorNumber: Int occupancy: OccupancySummary! }
The LevelOccupancy object provides a summary of parking availability on a specific floor or level of a parking facility. It includes identifiers for the level, zone, and project, along with the floor number and occupancy data to give an accurate view of parking status on that level.
Group Occupancy
type GroupOccupancy { id: ID! projectId: Int! zoneId: Int! levelId: Int! name: String groupType: String! customId: String locations: [Location!]! occupancy: OccupancySummary! }
The GroupOccupancy object provides an overview of parking availability for a group of spaces located on a single level within a zone. It includes a group type and identifiers for the project, zone, and level, along with the specific locations of each position within the group.
Position Occupancy
type PositionOccupancy { id: ID! customId: String groupId: Int! zoneId: Int! levelId: Int! projectId: Int! occupancyStatus: OccupancyStatus! statusChangeTime: AWSDateTime location: Location! labels: [String!] }
The PositionOccupancy object represents the real-time occupancy data of a specific parking space, indicating its current availability and when that status last changed. It includes detailed identifiers for its group, zone, level, and project, along with the space’s exact location.
Create Subscription Response
type CreateSubscriptionResponse { expiresOn: AWSDateTime! id: ID! }
A return type for all subscription creation and update mutations. Contains:
id
: A unique identifier used for subscribing to and listening for changes on this subscriptionexpiresOn
: The timestamp when the subscription will expire if not renewed
Occupancy Snapshot
Subscription Occupancy Snapshot
type SubscriptionOccupancySnapshot { filters: [OccupancySnapshot!]! id: ID! time: AWSDateTime! }
The return type when listening to a Snapshot subscription. Contains the complete state of all subscribed entities at the time of update.
Subscription Occupancy Updates
type SubscriptionOccupancyUpdates { group: GroupOccupancy id: ID! level: LevelOccupancy position: PositionOccupancy project: ProjectOccupancy time: AWSDateTime! zone: ZoneOccupancy }
The return type when listening to an Updates subscription. Contains only the changed entities since the last update.
Enums
Check Consistency
enum CheckConsistency { hierarchy }
Verifies that filters are consistent with the parking facility hierarchy:
hierarchy
: Ensures entities exist within their parent containers (e.g., zones within specified projects, groups within specified zones)
Group By
enum GroupBy { group level position project zone }
Determines the hierarchical level at which occupancy updates are aggregated:
group
: Aggregates at the parking group levellevel
: Aggregates at the floor/levelposition
: Provides individual parking space updatesproject
: Aggregates at the project levelzone
: Aggregates at the zone level
Labels Match
enum LabelsMatch { all any exclude }
Defines the matching logic for filtering positions by multiple labels:
all
: Matches positions that have all specified labelsany
: Matches positions that have at least one of the specified labelsexclude
: Matches positions that have none of the specified labels
Occupancy Status
enum OccupancyStatus { Free Occupied Undefined }
Represents the current status of a parking position:
Free
: Available parking spaceOccupied
: Space currently in useUndefined
: Status cannot be determined (e.g., device removed or experiencing errors)
Queries
In GraphQL, a Query is similar to a GET request in traditional APIs, where the client requests data from the server. Clients can pass query parameters to filter or specify the data they need, making queries customizable and efficient. Each query defines what fields to retrieve, and the server responds with only the requested data.
Project Queries
Example use case: A parking management company uses a centralized dashboard to monitor parking occupancy across multiple projects and locations. The dashboard provides a global view of parking availability and usage statistics, helping the company analyze trends, optimize capacity, and make informed decisions on pricing or resource allocation. The query retrieves occupancy data for multiple projects, showing total, available, and occupied spaces.
findProjectOccupancies( id: [Int!], lat: Float, lon: Float, distanceMeters: Int, networkId: [String!], groupId: [Int!], levelId: [Int!], labels: [String!], labelsMatch: LabelsMatch, floorNumber: [Int!], zoneId: [Int!], groupCustomId: [String!], limit: Int = 100, offset: Int = 0 ): [ProjectOccupancy]
{ findProjectOccupancies( projectId: [101, 202, 303] ) { id name location { lat lon } occupancy { total available occupied } } }
Zone Queries
Example use case: A mobile app helps drivers find available parking zones near their current location. The app allows users to specify the maximum distance from their location and shows the available parking spaces in nearby zones.
findZoneOccupancies( id: [Int!], lat: Float, lon: Float, distanceMeters: Int, networkId: [String!], groupId: [Int!], levelId: [Int!], labels: [String!], labelsMatch: LabelsMatch, floorNumber: [Int!], projectId: [Int!], groupCustomId: [String!], limit: Int = 100, offset: Int = 0 ): [ZoneOccupancy]
{ findZoneOccupancies( lat: 37.7749, lon: -122.4194, distanceMeters: 5000, ) { name location { lat lon } occupancy { available } } }
Level Queries
Example use case: A parking facility is equipped with digital signage to help drivers locate available parking spaces on different levels. The digital signage system periodically queries the parking management system to retrieve the latest occupancy data for each level and displays the number of available spots on each floor. The system filters by specific zone and project IDs to ensure it only retrieves data for relevant sections of the parking facility.
findLevelOccupancies( id: [Int!], lat: Float, lon: Float, distanceMeters: Int, networkId: [String!], groupId: [Int!], labels: [String!], labelsMatch: LabelsMatch, floorNumber: [Int!], zoneId: [Int!], projectId: [Int!], groupCustomId: [String!], limit: Int = 100, offset: Int = 0 ): [LevelOccupancy]
{ findLevelOccupancies( zoneId: [5], projectId: [101], floorNumber: [1, 2, 3], ) { id name floorNumber occupancy { total available occupied } } }
Group Queries
Example use case: A city has implemented digital signage to guide drivers to available EV parking spots in outdoor street parking zones. The system uses parking occupancy data filtered for groups of EV-designated spots and displays the availability on signage near high-traffic areas. This reduces emissions by minimizing the time drivers spend searching for EV parking, thus supporting the city’s environmental goals.
findGroupOccupancies( id: [Int!], lat: Float, lon: Float, distanceMeters: Int, networkId: [String!], levelId: [Int!], labels: [String!], labelsMatch: LabelsMatch, floorNumber: [Int!], zoneId: [Int!], projectId: [Int!], groupCustomId: [String!], limit: Int = 100, offset: Int = 0 ): [GroupOccupancy]
{ findGroupOccupancies( labels: ["EV"], labelsMatch: any, zoneId: [10, 22], ) { id name groupType locations { lat lon } occupancy { total available occupied } } }
Position Queries
Example use case: A mobile app is designed to help drivers locate and navigate to individual parking spots within a parking facility. The app displays a map that shows the exact location of available parking spaces and guides the driver to the nearest free spot. The query retrieves data for individual parking positions, including their occupancy status and location, allowing the app to highlight available spaces on the map.
findPositionOccupancies( id: [String!], lat: Float, lon: Float, distanceMeters: Int, groupId: [Int!], levelId: [Int!], labels: [String!], labelsMatch: LabelsMatch, floorNumber: [Int!], zoneId: [Int!], projectId: [Int!], groupCustomId: [String!], limit: Int = 100, offset: Int = 0 ): [PositionOccupancy]
{ findPositionOccupancies( lat: 40.7128, lon: -74.0060, distanceMeters: 500, labels: ["EV"], labelsMatch: any, zoneId: [7] ) { id location { lat lon } occupancyStatus statusChangeTime }
Mutations and Subscriptions
Subscrption Types
This API enables the creation of subscriptions and provides real-time updates about these subscriptions. There are two types of subscriptions available:
Snapshot Subscriptions: Provide complete occupancy data for the subscribed entities whenever any change occurs.
Update Subscriptions: Provide only the changed occupancy data, making them more efficient for real-time updates.
In GraphQL, a mutation is used to modify or create data on the server, similar to POST, PUT, or DELETE requests in REST APIs. In our use case, mutations are used to create and update subscriptions, allowing clients to manage parking-related subscriptions.
Subscription expiry
The subscription expiry time is automatically extended after each update. To extend a subscription without changing its parameters you should use the keepAliveSubscription mutation.
Create Snapshot Subscription
Example use case: This mutation creates a subscription to monitor parking availability in zones 10 and 11 within project 101. It uses a zone-based grouping (groupBy: zone
) and filters for parking spaces labeled as EV. The subscription is limited to a 5,000-meter radius around the given coordinates (latitude: 37.7749, longitude: -122.4194). The labelsMatch: any
ensures that any space marked as EV will be included. The mutation response will include a unique subscription id
and an expiresOn
field, indicating when the subscription will expire.
createSubscriptionOccupancySnapshot( filters: [FilterInput!]!, checkConsistency: [CheckConsistency!] ): CreateSubscriptionResponse!
mutation { createSubscriptionOccupancySnapshot( filters: [{ projectId: [101], zoneId: [10, 11], area: { latitude: 37.7749, longitude: -122.4194, distanceMeters: 5000 }, labels: ["EV"], labelsMatch: any, groupBy: zone }] ) { id expiresOn } }
Create Updates Subscription
Example use case: When monitoring a large parking facility, receiving only changed data can significantly reduce bandwidth usage. This subscription will only return information about spaces that have changed their occupancy status.
createSubscriptionOccupancyUpdates( projectId: [Int!], zoneId: [Int!], levelId: [Int!], floorNumber: [Int!], groupId: [Int!], groupCustomId: [String!], networkId: [String!], labels: [String!], labelsMatch: LabelsMatch, area: LocationWithRadiusInput, groupBy: GroupBy!, checkConsistency: [CheckConsistency!] ): CreateSubscriptionResponse!
mutation { createSubscriptionOccupancyUpdates( projectId: [101], zoneId: [10, 11], area: { latitude: 37.7749, longitude: -122.4194, distanceMeters: 5000 }, labels: ["EV"], labelsMatch: any, groupBy: zone ) { id expiresOn } }
Subscription Updates Warning
Important: Sending an empty update will result in clearing all subscription parameters, which may disrupt the functionality of the subscription. Always ensure to include the full set of original parameters when extending or modifying a subscription to avoid unintended data loss or disruptions.
Update Snapshot Subscription
Example use case: In this mobile app scenario, the user initially subscribed to parking availability within a 5,000-meter radius, but as they zoom in on the map, the subscription needs to be adjusted to a smaller area.
mutation { updateSubscriptionOccupancySnapshot( id: "12345", filters: [{ projectId: [101], zoneId: [10, 11], area: { latitude: 37.7749, longitude: -122.4194, distanceMeters: 1000 # Updated }, labels: ["EV"], labelsMatch: any, groupBy: position # Updated }] ) { id expiresOn } }
Update Updates Subscription
Similar to updating snapshot subscriptions, but for the more efficient updates-only subscription
mutation { updateSubscriptionOccupancyUpdates( id: "12345", projectId: [101], zoneId: [10, 11], area: { latitude: 37.7749, longitude: -122.4194, distanceMeters: 1000 }, labels: ["EV"], labelsMatch: any, groupBy: position ) { id expiresOn } }
Demand Snapshot
Example use case: During testing or debugging, you may need to manually trigger a complete data update.
mutation { demandSubscriptionOccupancySnapshot(id: "12345") }
Keep Alive Subscirption
Extends the expiration time of any subscription without modifying its parameters.
mutation { keepAliveSubscription(id: "12345") { id expiresOn } }