openapi: 3.0.3
info:
title: Device Geofencing Subscriptions
description: |
API to create, retrieve and delete event subscriptions to realize geofencing for a user device.
# Introduction
With this API, API consumers can create subscriptions for their devices to receive notifications when a device enters or exits a specified area.
* If the provided area is out of the operator's coverage or it is not supported for any reason, an error `422 GEOFENCING_SUBSCRIPTIONS.AREA_NOT_COVERED` will be returned.
* Legal restrictions regarding privacy, or other regulatory or implementation issues, may force the operator to set restrictions to the provided area, such as setting a minimum value to the accepted radius. In these cases, an error `422 GEOFENCING_SUBSCRIPTIONS.INVALID_AREA` will be returned and the error message will refer to the reason of the limitation.
The area provided in the request is described by a circle determined by coordinates (latitude and longitude) and an accuracy defined by the radius.
Upon successfully creating a subscription, the API will provide an Event Subscription ID, and it will indicate the subscription's expiration date.
If the geofencing-state of a device changes, the event subscriber will be notified back to the provided Notification-Url given by the subscription-request.
Device geofencing API could be useful in scenarios such as:
- Tracking devices for Presetting of Home-Settings
- Tracking of logistics
# Relevant terms and definitions
* **Device**: A device refers to any physical entity that can connect to a network and participate in network communication.
* **Area**: It specifies the geographical surface which a device is planned to enter or exit.
# API Functionality
The API exposes following capabilities:
## Device Geofencing subscription
These endpoints allow to manage event subscription on geofencing device location event.
The CAMARA subscription model is detailed in the CAMARA API design guideline document and follows CloudEvents specification.
It is mandatory in the subscription to provide the event `type` for which the client would like to subscribe.
Following event ``types`` are managed for this API:
- ``org.camaraproject.geofencing-subscriptions.v0.area-entered`` - Event triggered when the device enters the given area
- ``org.camaraproject.geofencing-subscriptions.v0.area-left`` - Event triggered when the device leaves the given area
Note: Additionally, the following events could be send, which do not require a dedicated subscription:
- `org.camaraproject.geofencing-subscriptions.v0.subscription-started` is sent when the subscription starts.
- `org.camaraproject.geofencing-subscriptions.v0.subscription-updated` is sent when the subscription is updated.
- `org.camaraproject.geofencing-subscriptions.v0.subscription-ended` is sent when the subscription ends.
It is used when:
- the subscription expire time (optionally set by the requester) has been reached
- the maximum number of subscription events (optionally set by the requester) has been reached
- the subscription was deleted by the requester
- the Access Token `sinkCredential` (optionally set by the requester) expiration time has been reached
- the API server has to stop sending notification prematurely
- the specified geofence-`area` cannot be covered or is too small to be managed
### Notification callback
This endpoint describes the event notification received on subscription listener side when the event occurred.
As for subscription, detailed description of the event notification is provided in the CAMARA API design guideline document.
_**WARNING**: This callback endpoint must be exposed on the consumer side as `POST /{$request.body#/sink}`.
Developers may provide a callback URL on which notifications regarding geofencing can be received from the service provider.
If an event occurs the application will send events to the provided webhook - 'sink'._
# Authorization and authentication
The "Camara Security and Interoperability Profile" provides details of how an API consumer requests an access token. Please refer to Identity and Consent Management (https://github.com/camaraproject/IdentityAndConsentManagement/) for the released version of the profile.
The specific authorization flows to be used will be agreed upon during the onboarding process, happening between the API consumer and the API provider, taking into account the declared purpose for accessing the API, whilst also being subject to the prevailing legal framework dictated by local legislation.
In cases where personal data is processed by the API and users can exercise their rights through mechanisms such as opt-in and/or opt-out, the use of three-legged access tokens is mandatory. This ensures that the API remains in compliance with privacy regulations, upholding the principles of transparency and user-centric privacy-by-design.
# Identifying the device from the access token
This API requires the API consumer to identify a device as the subject of the API as follows:
- When the API is invoked using a two-legged access token, the subject will be identified from the optional `device` object, which therefore MUST be provided.
- When a three-legged access token is used however, this optional identifier MUST NOT be provided, as the subject will be uniquely identified from the access token.
This approach simplifies API usage for API consumers using a three-legged access token to invoke the API by relying on the information that is associated with the access token and was identified during the authentication process.
## Error handling:
- If the subject cannot be identified from the access token and the optional `device` object is not included in the request, then the server will return an error with the `422 MISSING_IDENTIFIER` error code.
- If the subject can be identified from the access token and the optional `device` object is also included in the request, then the server will return an error with the `422 UNNECESSARY_IDENTIFIER` error code. This will be the case even if the same device is identified by these two methods, as the server is unable to make this comparison.
# Multi-SIM scenario handling
In multi-SIM scenarios, where more than one mobile device is associated with the phone number given as input in the API call (e.g. a smartphone with an associated smartwatch), it might not be possible to uniquely identify the device whose location is to be verified. Check with the API provider what is the expected behaviour when a phone number belonging to a multi-SIM group is used as the device identifier, as the API may behave:
- rejecting the subscription with an error indicating that that phone number is not supported for this API, or
- generating events for a single device in the multi-SIM group, if one of the devices is considered linked to the main SIM and this concept is supported by the operator, or
- generating events for of all the SIMs associated to the requested phone number.
Possible solutions to make the scenario more deterministic include:
- Using preferably the authorisation code flow to obtain an access token, which will automatically identify the intended device.
- Identifying the intended device from a unique identifier for that device, such as its source IP address and port.
- Check with the API provider whether a unique "secondary" phone number is already associated with each device and use the secondary phone number to identify the intended device if available.
# Additional CAMARA error responses
The list of error codes in this API specification is not exhaustive. Therefore the API specification may not document some non-mandatory error statuses as indicated in `CAMARA API Design Guide`.
Please refer to the `CAMARA_common.yaml` of the Commonalities Release associated to this API version for a complete list of error responses. The applicable Commonalities Release can be identified in the `API Readiness Checklist` document associated to this API version.
As a specific rule, error `501 - NOT_IMPLEMENTED` can be only a possible error response if it is explicitly documented in the API.
# Further info and support
(FAQs will be added in a later version of the documentation)
version: wip
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
x-camara-commonalities: tbd
externalDocs:
description: Project documentation at Camara
url: https://github.com/camaraproject/DeviceLocation
servers:
- url: "{apiRoot}/geofencing-subscriptions/vwip"
variables:
apiRoot:
default: http://localhost:9091
description: API root
tags:
- name: Geofencing subscriptions
description: Operations to manage event subscriptions on geofencing events for leaving and entering an area.
paths:
/subscriptions:
post:
tags:
- Geofencing subscriptions
summary: "Create a geofencing subscription for a device"
description: Create a subscription for a device to receive notifications when the device enters or exits a specified area.
operationId: createGeofencingSubscription
parameters:
- $ref: "#/components/parameters/x-correlator"
security:
- openId:
- geofencing-subscriptions:org.camaraproject.geofencing-subscriptions.v0.area-entered:create
- geofencing-subscriptions:org.camaraproject.geofencing-subscriptions.v0.area-left:create
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/SubscriptionRequest"
examples:
CIRCLE_AREA_ENTERED:
$ref: "#/components/examples/REQUEST_CIRCLE_AREA_ENTERED"
REQUEST_CIRCLE_AREA_ENTERED_MULTIPLE_IDENTIFIERS:
$ref: "#/components/examples/REQUEST_CIRCLE_AREA_ENTERED_MULTIPLE_IDENTIFIERS"
required: true
callbacks:
notifications:
"{$request.body#/sink}":
post:
summary: "notifications callback"
description: |
Important: This endpoint is to be implemented by the API consumer.
The Geofencing server will call this endpoint whenever a Geofencing event occurs.
The `operationId` value will have to be replaced accordingly with WG API specific semantics.
operationId: postNotification
parameters:
- $ref: "#/components/parameters/x-correlator"
requestBody:
required: true
content:
application/cloudevents+json:
schema:
$ref: "#/components/schemas/CloudEvent"
examples:
CIRCLE_AREA_ENTERED:
$ref: "#/components/examples/CIRCLE_AREA_ENTERED"
CIRCLE_AREA_LEFT:
$ref: "#/components/examples/CIRCLE_AREA_LEFT"
SUBSCRIPTION_STARTED:
$ref: "#/components/examples/SUBSCRIPTION_STARTED"
SUBSCRIPTION_UPDATED:
$ref: "#/components/examples/SUBSCRIPTION_UPDATED"
SUBSCRIPTION_ENDED:
$ref: "#/components/examples/SUBSCRIPTION_ENDED"
SUBSCRIPTION_UNPROCESSABLE:
$ref: "#/components/examples/SUBSCRIPTION_UNPROCESSABLE"
responses:
"204":
description: Successful notification.
headers:
x-correlator:
$ref: "#/components/headers/x-correlator"
"400":
$ref: "#/components/responses/Generic400"
"401":
$ref: "#/components/responses/Generic401"
"403":
$ref: "#/components/responses/Generic403"
"410":
$ref: "#/components/responses/Generic410"
"429":
$ref: "#/components/responses/Generic429"
security:
- notificationsBearerAuth: []
- {}
responses:
"201":
description: Created (Successful creation of subscription).
headers:
x-correlator:
$ref: "#/components/headers/x-correlator"
content:
application/json:
schema:
$ref: "#/components/schemas/Subscription"
"202":
description: Request accepted to be processed. It applies for async creation process.
headers:
x-correlator:
$ref: "#/components/headers/x-correlator"
content:
application/json:
schema:
$ref: "#/components/schemas/SubscriptionAsync"
"400":
$ref: "#/components/responses/CreateSubscriptionBadRequest400"
"401":
$ref: "#/components/responses/Generic401"
"403":
$ref: "#/components/responses/Generic403"
"422":
$ref: "#/components/responses/CreateSubscriptionUnprocessableEntity422"
"429":
$ref: "#/components/responses/Generic429"
get:
tags:
- Geofencing subscriptions
summary: "Retrieve a list of geofencing event subscription"
description: Retrieve a list of geofencing event subscription(s).
operationId: retrieveGeofencingSubscriptionList
parameters:
- $ref: "#/components/parameters/x-correlator"
security:
- openId:
- geofencing-subscriptions:read
responses:
"200":
description: List of event subscription details.
headers:
x-correlator:
$ref: "#/components/headers/x-correlator"
content:
application/json:
schema:
type: array
minItems: 0
items:
$ref: "#/components/schemas/Subscription"
"400":
$ref: "#/components/responses/Generic400"
"401":
$ref: "#/components/responses/Generic401"
"403":
$ref: "#/components/responses/Generic403"
/subscriptions/{subscriptionId}:
get:
tags:
- Geofencing subscriptions
summary: "Operation to retrieve a subscription based on the provided ID"
description: Retrieve Geofencing subscription information for a given subscription ID.
operationId: retrieveGeofencingSubscription
security:
- openId:
- geofencing-subscriptions:read
parameters:
- $ref: "#/components/parameters/SubscriptionId"
- $ref: "#/components/parameters/x-correlator"
responses:
"200":
description: OK
headers:
x-correlator:
$ref: "#/components/headers/x-correlator"
content:
application/json:
schema:
$ref: "#/components/schemas/Subscription"
"400":
$ref: "#/components/responses/SubscriptionIdRequired"
"401":
$ref: "#/components/responses/Generic401"
"403":
$ref: "#/components/responses/Generic403"
"404":
$ref: "#/components/responses/Generic404"
delete:
tags:
- Geofencing subscriptions
summary: "Delete a Geofencing event subscription"
operationId: deleteGeofencingSubscription
description: Delete a given Geofencing subscription.
security:
- openId:
- geofencing-subscriptions:delete
parameters:
- $ref: "#/components/parameters/SubscriptionId"
- $ref: "#/components/parameters/x-correlator"
responses:
"204":
description: Geofencing subscription deleted.
headers:
x-correlator:
$ref: "#/components/headers/x-correlator"
"202":
description: Request accepted to be processed. It applies for async deletion process.
headers:
x-correlator:
$ref: "#/components/headers/x-correlator"
content:
application/json:
schema:
$ref: "#/components/schemas/SubscriptionAsync"
"400":
$ref: "#/components/responses/SubscriptionIdRequired"
"401":
$ref: "#/components/responses/Generic401"
"403":
$ref: "#/components/responses/Generic403"
"404":
$ref: "#/components/responses/Generic404"
components:
securitySchemes:
openId:
description: OpenID Connect authentication.
type: openIdConnect
openIdConnectUrl: https://example.com/.well-known/openid-configuration
notificationsBearerAuth:
type: http
scheme: bearer
bearerFormat: "{$request.body#/sinkCredential.credentialType}"
parameters:
SubscriptionId:
name: subscriptionId
in: path
description: Subscription identifier that was obtained from the create event subscription operation.
required: true
schema:
$ref: "#/components/schemas/SubscriptionId"
x-correlator:
name: x-correlator
in: header
description: Correlation id for the different services.
schema:
$ref: "#/components/schemas/XCorrelator"
headers:
x-correlator:
description: Correlation id for the different services.
schema:
$ref: "#/components/schemas/XCorrelator"
schemas:
XCorrelator:
type: string
pattern: ^[a-zA-Z0-9-_:;.\/<>{}]{0,256}$
example: "b4333c46-49c0-4f62-80d7-f0ef930f1c46"
ErrorInfo:
description: The error info object for possible error cases.
type: object
required:
- status
- code
- message
properties:
status:
type: integer
description: HTTP response status code.
code:
type: string
description: A human-readable code to describe the error.
message:
type: string
description: A human-readable description of what the event represents.
SubscriptionRequest:
description: The request for creating an event-type event subscription.
type: object
required:
- sink
- protocol
- config
- types
properties:
protocol:
$ref: "#/components/schemas/Protocol"
sink:
type: string
format: uri
pattern: ^https:\/\/.+$
description: The address to which events shall be delivered using the selected protocol.
example: "https://endpoint.example.com/sink"
sinkCredential:
$ref: "#/components/schemas/SinkCredential"
types:
description: |
Camara Event types which are eligible to be delivered by this subscription.
Note: As of now we enforce to have only event type per subscription.
type: array
minItems: 1
maxItems: 1
items:
$ref: "#/components/schemas/SubscriptionEventType"
config:
$ref: "#/components/schemas/ConfigRequest"
discriminator:
propertyName: protocol
mapping:
HTTP: "#/components/schemas/HTTPSubscriptionRequest"
MQTT3: "#/components/schemas/MQTTSubscriptionRequest"
MQTT5: "#/components/schemas/MQTTSubscriptionRequest"
AMQP: "#/components/schemas/AMQPSubscriptionRequest"
NATS: "#/components/schemas/NATSSubscriptionRequest"
KAFKA: "#/components/schemas/ApacheKafkaSubscriptionRequest"
Protocol:
type: string
enum: ["HTTP", "MQTT3", "MQTT5", "AMQP", "NATS", "KAFKA"]
description: Identifier of a delivery protocol. Only HTTP is allowed for now.
example: "HTTP"
Config:
description: |
Implementation-specific configuration parameters are needed by the subscription manager for acquiring events.
In CAMARA we have predefined attributes like `subscriptionExpireTime`, `subscriptionMaxEvents`, `initialEvent`.
type: object
properties:
subscriptionExpireTime:
type: string
format: date-time
example: "2023-01-17T13:18:23.682Z"
description: The subscription expiration time (in date-time format) requested by the API consumer. It must follow [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) and must have time zone.
subscriptionMaxEvents:
type: integer
description: |
Identifies the maximum number of event reports to be generated (>=1) requested by the API consumer - Once this number is reached, the subscription ends.
Note on combined usage of `initialEvent` and `subscriptionMaxEvents`:
If an event is triggered following `initialEvent` set to `true`, this event will be counted towards `subscriptionMaxEvents`.
minimum: 1
example: 5
initialEvent:
type: boolean
description: |
Set to `true` by API consumer if consumer wants to get an event as soon as the subscription is created and current situation reflects event request.
Example: Consumer request area entered event. If consumer sets initialEvent to true and device is already in the geofence, an event is triggered.
ConfigRequest:
allOf:
- $ref: "#/components/schemas/Config"
- description: |
Implementation-specific configuration parameters are needed by the subscription manager for acquiring events.
In CAMARA we have predefined attributes like `subscriptionExpireTime`, `subscriptionMaxEvents`, `initialEvent`.
Specific event type attributes requested by the client must be defined in `subscriptionDetail`.
Note: If a request is performed for several event types, all subscribed events will use the same `config` parameters.
type: object
required:
- subscriptionDetail
properties:
subscriptionDetail:
$ref: "#/components/schemas/SubscriptionDetailRequest"
ConfigResponse:
allOf:
- $ref: "#/components/schemas/Config"
- description: |
Implementation-specific configuration parameters are needed by the subscription manager for acquiring events.
In CAMARA we have predefined attributes like `subscriptionExpireTime`, `subscriptionMaxEvents`, `initialEvent`.
Specific event type attributes granted by the implementation are included in `subscriptionDetail`.
Note: If a request is performed for several event types, all subscribed events will use the same `config` parameters.
type: object
required:
- subscriptionDetail
properties:
subscriptionDetail:
$ref: "#/components/schemas/SubscriptionDetailResponse"
SinkCredential:
type: object
description: A sink credential provides authentication or authorization information necessary to enable delivery of events to a target.
properties:
credentialType:
type: string
description: |
The type of the credential.
Note: Type of the credential - MUST be set to ACCESSTOKEN for now
enum:
- PLAIN
- ACCESSTOKEN
- REFRESHTOKEN
discriminator:
propertyName: credentialType
mapping:
PLAIN: "#/components/schemas/PlainCredential"
ACCESSTOKEN: "#/components/schemas/AccessTokenCredential"
REFRESHTOKEN: "#/components/schemas/RefreshTokenCredential"
required:
- credentialType
PlainCredential:
type: object
description: A plain credential as a combination of an identifier and a secret.
allOf:
- $ref: "#/components/schemas/SinkCredential"
- type: object
required:
- identifier
- secret
properties:
identifier:
description: The identifier might be an account or username.
type: string
secret:
description: The secret might be a password or passphrase.
type: string
AccessTokenCredential:
type: object
description: An access token credential.
allOf:
- $ref: "#/components/schemas/SinkCredential"
- type: object
properties:
accessToken:
description: REQUIRED. An access token is a previously acquired token granting access to the target resource.
type: string
accessTokenExpiresUtc:
type: string
format: date-time
description: |
REQUIRED. An absolute (UTC) timestamp at which the token shall be considered expired.
In the case of an ACCESS_TOKEN_EXPIRED termination reason, implementation should notify the client before the expiration date.
If the access token is a JWT and registered "exp" (Expiration Time) claim is present, the two expiry times should match.
It must follow [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) and must have time zone.
example: "2023-07-03T12:27:08.312Z"
accessTokenType:
description: REQUIRED. Type of the access token (See [OAuth 2.0](https://tools.ietf.org/html/rfc6749#section-7.1)).
type: string
enum:
- bearer
required:
- accessToken
- accessTokenExpiresUtc
- accessTokenType
RefreshTokenCredential:
type: object
description: An access token credential with a refresh token.
allOf:
- $ref: "#/components/schemas/SinkCredential"
- type: object
properties:
accessToken:
description: REQUIRED. An access token is a previously acquired token granting access to the target resource.
type: string
accessTokenExpiresUtc:
type: string
format: date-time
description: |
REQUIRED. An absolute (UTC) timestamp at which the token shall be considered expired.
In the case of an ACCESS_TOKEN_EXPIRED termination reason, implementation should notify the client before the expiration date.
If the access token is a JWT and registered "exp" (Expiration Time) claim is present, the two expiry times should match.
It must follow [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) and must have time zone.
accessTokenType:
description: REQUIRED. Type of the access token (See [OAuth 2.0](https://tools.ietf.org/html/rfc6749#section-7.1)).
type: string
enum:
- bearer
refreshToken:
description: REQUIRED. An refresh token credential used to acquire access tokens.
type: string
refreshTokenEndpoint:
type: string
format: uri
description: REQUIRED. A URL at which the refresh token can be traded for an access token.
required:
- accessToken
- accessTokenExpiresUtc
- accessTokenType
- refreshToken
- refreshTokenEndpoint
SubscriptionDetailRequest:
description: The detail of the requested event subscription.
type: object
properties:
device:
$ref: "#/components/schemas/Device"
area:
$ref: "#/components/schemas/Area"
required:
- area
SubscriptionDetailResponse:
description: The detail of the event subscription granted by the implementation.
type: object
properties:
device:
$ref: "#/components/schemas/DeviceResponse"
area:
$ref: "#/components/schemas/Area"
required:
- area
SubscriptionEventType:
type: string
description: |
area-entered - Event triggered when the device enters the given area
area-left - Event triggered when the device leaves the given area
enum:
- org.camaraproject.geofencing-subscriptions.v0.area-entered
- org.camaraproject.geofencing-subscriptions.v0.area-left
NotificationEventType:
type: string
description: |
area-entered - Event triggered when the device enters the given area
area-left - Event triggered when the device leaves the given area
subscription-started - Event triggered when the subscription starts.
subscription-updated - Event triggered when the subscription is updated.
subscription-ended - Event triggered when the subscription ends
enum:
- org.camaraproject.geofencing-subscriptions.v0.area-entered
- org.camaraproject.geofencing-subscriptions.v0.area-left
- org.camaraproject.geofencing-subscriptions.v0.subscription-started
- org.camaraproject.geofencing-subscriptions.v0.subscription-updated
- org.camaraproject.geofencing-subscriptions.v0.subscription-ended
Subscription:
description: Represents a event-type subscription.
type: object
required:
- sink
- protocol
- config
- types
- id
- startsAt
properties:
protocol:
$ref: "#/components/schemas/Protocol"
sink:
type: string
format: uri
pattern: ^https:\/\/.+$
description: The address to which events shall be delivered using the selected protocol.
example: "https://endpoint.example.com/sink"
types:
description: |
Camara Event types eligible to be delivered by this subscription.
Note: As of now we enforce to have only event type per subscription.
type: array
minItems: 1
maxItems: 1
items:
$ref: "#/components/schemas/SubscriptionEventType"
config:
$ref: "#/components/schemas/ConfigResponse"
id:
type: string
description: The unique identifier of the subscription in the scope of the subscription manager. When this information is contained within an event notification, this concept SHALL be referred as subscriptionId as per Commonalities Event Notification Model.
example: "1119920371"
startsAt:
type: string
format: date-time
description: |
Date when the event subscription will begin/began
It must follow [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) and must have time zone.
example: "2023-07-03T12:27:08.312Z"
expiresAt:
type: string
format: date-time
description: |
Date when the event subscription will expire. Only provided when `subscriptionExpireTime` is indicated by API client or Telco Operator has specific policy about that.
It must follow [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) and must have time zone.
example: "2023-07-03T12:27:08.312Z"
status:
type: string
description: |-
Current status of the subscription - Management of Subscription State engine is not mandatory for now. Note not all statuses may be considered to be implemented. Details:
- `ACTIVATION_REQUESTED`: Subscription creation (POST) is triggered but subscription creation process is not finished yet.
- `ACTIVE`: Subscription creation process is completed. Subscription is fully operative.
- `INACTIVE`: Subscription is temporarily inactive, but its workflow logic is not deleted.
- `EXPIRED`: Subscription is ended (no longer active). This status applies when subscription is ended due to `SUBSCRIPTION_EXPIRED` or `ACCESS_TOKEN_EXPIRED` event.
- `DELETED`: Subscription is ended as deleted (no longer active). This status applies when subscription information is kept (i.e. subscription workflow is no longer active but its meta-information is kept).
enum:
- ACTIVATION_REQUESTED
- ACTIVE
- EXPIRED
-
# --- truncated at 32 KB (71 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/orange-business/refs/heads/main/openapi/orange-business-geofencing-openapi.yml